V14 QA added navigation integration tests (#16973)

* Tests

* Remove props and use local vars

* Adding preliminary navigation service and content implementation

* Adding preliminary unit tests

* Change from async methods

* Refactor GetParentKey to TryGetParentKey

* Refactor GetChildrenKeys to TryGetChildrenKeys

* Refactor GetDescendantsKeys to TryGetDescendantsKeys

* Refactor GetAncestorsKeys to TryGetAncestorsKeys

* Refactor GetSiblingsKeys to TryGetSiblingsKeys

* Refactor TryGetChildrenKeys

* Initial integration tests

* Use ContentEditingService instead of ContentService

* Remove INavigationService.Copy implementation and unit tests

* Rename var

* Adding clarification

* Initial ContentNavigationRepository

* Initial NavigationFactory

* Remove filtering from factory

* NavigationRepository and implementation

* InitializationService responsible for seeding the in-memory structure

* Register repository and service

* Adding NavigationDto and NavigationNode

* Adding INavigationService dependency and Enlist updating navigation structure actions

* Documentation

* Adding tests for removing descendants as well

* Changed to ConcurrentDictionary

* Remove keys comments for tests

* Adding documentation

* Forgotten ConcurrentDictionary change

* Isolating the operations on the model

* Splitting the INavigationService to separate the querying from the managing functionality

* Introducing specific navigation services for document, document recycle bin, media and media recycle bin

* Making ContentNavigationService into a base as the functionality will be shared between the document, document recycle bin, media and media recycle bin services

* Adding the implementations of document, document recycle bin, media and media recycle bin navigation services

* Fixing comments

* Initializing all 4 collections

* Adapting the navigation unit tests to the base now

* Adapting integration tests to specific navigation service

* Adding test for rebuilding the structure

* Adding implementation for Adding and Getting a node - needed for moving to and restoring from the recycle bin + tests

* Updating the document navigation structure from the ContentService

* Fix typo

* Adding trashed items implementation in base - currently managing 2 structures

* Removing no longer relevant GetNavigationNode and AddNavigationNode

* Fix removing parent when child is removed supporting methods

* Added restoring functionality

* Adding Bin functionality to DocumentNavigationService

* Removing Move signature from IDocumentNavigationService

* Adding RecycleBin query and management services

* Re-adding Move and removing GetNavigationNode and AddNavigationNode signatures from interface

* Rebuilding bin structure using _documentNavigationService, instead of _documentRecycleBinNavigationService

* Fixing test name

* Adding more tests for remove

* Adding tests for restore and removing ones for GetNavigationNode and AddNavigationNode

* Remove comments

* Removing document and media RecycleBinNavigationService and their interfaces

* Adding media rebuild bin

* Fixing initialization with correct interfaces

* Removing RecycleBinNavigationServices' registration

* Remove IDocumentRecycleBinNavigationService dependency

* Updating in-memory nav structure when content updates happen

* Adding the rest of the integration tests

* Clean up IMediaNavigationService

* Fix comments

* Remove CustomTestSetup in integration tests as the structure is updated when content updates happen

* Adding and fixing comments

* Making RebuildBinAsync abstract as well

* Adding DocumentNavigationServiceTestsBase

* Splitting DocumentNavigationServiceTests into partial test classes

* Cleaning up DocumentNavigationServiceTests since tests have been moved to specific partial classes

* Reuse a method for creating content in tests

* Change type in test base

* Adding navigation structure updates in media service

* Adding MediaNavigationServiceTestsBase

* Adding integration tests for media nav str

* Remove services as we will have more concrete ones

* Add document and media IXNavigationQueryService and IXNavigationManagementService

* Inject ManagementService in ContentService.cs and MediaService.cs

* Change implementation to implement the new services + registration

* Make classes sealed

* Inject correct services in InitializationService

* Using the right services in integration tests

* Adding comments

* Removing bin interfaces from main navigation ones

* Rename Remove to MoveToBin

* Added tests for Copy

* Added additional tests

* Split test

* Added Media tests

* Updated and added content tests

* Cleaned up naming

* Cleaned up

* Rename initialization service to initialization hosted service

* Refactor repository to return a collection

* Add interface for the NavigationDto

* Add constants to bind property names between DTOs

* Move factory and fix input type

* Use constants for column names

* Use factory from base

* Fixed indentation

* Fix bug when rebuilding the recycle bin structure

* Fix comments

* Fix merged in code

* Fix bug again after merge

* Minor things

---------

Co-authored-by: Elitsa <elm@umbraco.dk>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Andreas Zerbst
2024-09-25 08:33:23 +02:00
committed by GitHub
parent cd9979bed7
commit 71d6b45a95
19 changed files with 654 additions and 70 deletions

View File

@@ -11,10 +11,10 @@ internal static class NavigationFactory
/// </summary>
/// <param name="nodesStructure">A dictionary of <see cref="NavigationNode" /> objects with key corresponding to their unique Guid.</param>
/// <param name="entities">The <see cref="INavigationModel" /> objects used to build the navigation nodes dictionary.</param>
public static void BuildNavigationDictionary(ConcurrentDictionary<Guid, NavigationNode> nodesStructure,IEnumerable<INavigationModel> entities)
public static void BuildNavigationDictionary(ConcurrentDictionary<Guid, NavigationNode> nodesStructure, IEnumerable<INavigationModel> entities)
{
var entityList = entities.ToList();
var idToKeyMap = entityList.ToDictionary(x => x.Id, x => x.Key);
Dictionary<int, Guid> idToKeyMap = entityList.ToDictionary(x => x.Id, x => x.Key);
foreach (INavigationModel entity in entityList)
{

View File

@@ -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<Guid> rootKeys)
=> TryGetRootKeysFromStructure(_navigationStructure, out rootKeys);
public bool TryGetChildrenKeys(Guid parentKey, out IEnumerable<Guid> childrenKeys)
=> TryGetChildrenKeysFromStructure(_navigationStructure, parentKey, out childrenKeys);
public bool TryGetRootKeys(out IEnumerable<Guid> childrenKeys)
=> TryGetRootKeysFromStructure(_navigationStructure, out childrenKeys);
public bool TryGetDescendantsKeys(Guid parentKey, out IEnumerable<Guid> descendantsKeys)
=> TryGetDescendantsKeysFromStructure(_navigationStructure, parentKey, out descendantsKeys);
@@ -165,7 +165,6 @@ internal abstract class ContentNavigationServiceBase
_recycleBinNavigationStructure.TryRemove(key, out _);
}
/// <summary>
/// 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<INavigationModel> navigationModels = trashed ?
_navigationRepository.GetTrashedContentNodesByObjectType(objectTypeKey) :
_navigationRepository.GetContentNodesByObjectType(objectTypeKey);
NavigationFactory.BuildNavigationDictionary(_navigationStructure, navigationModels);
// Build the corresponding navigation structure
if (trashed)
{
IEnumerable<INavigationModel> navigationModels = _navigationRepository.GetTrashedContentNodesByObjectType(objectTypeKey);
NavigationFactory.BuildNavigationDictionary(_recycleBinNavigationStructure, navigationModels);
}
else
{
IEnumerable<INavigationModel> navigationModels = _navigationRepository.GetContentNodesByObjectType(objectTypeKey);
NavigationFactory.BuildNavigationDictionary(_navigationStructure, navigationModels);
}
}
private bool TryGetParentKeyFromStructure(ConcurrentDictionary<Guid, NavigationNode> structure, Guid childKey, out Guid? parentKey)
@@ -204,6 +209,13 @@ internal abstract class ContentNavigationServiceBase
return false;
}
private bool TryGetRootKeysFromStructure(ConcurrentDictionary<Guid, NavigationNode> structure, out IEnumerable<Guid> 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<Guid, NavigationNode> structure, Guid parentKey, out IEnumerable<Guid> childrenKeys)
{
if (structure.TryGetValue(parentKey, out NavigationNode? parentNode) is false)
@@ -217,13 +229,6 @@ internal abstract class ContentNavigationServiceBase
return true;
}
private bool TryGetRootKeysFromStructure(ConcurrentDictionary<Guid, NavigationNode> structure, out IEnumerable<Guid> 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<Guid, NavigationNode> structure, Guid parentKey, out IEnumerable<Guid> descendantsKeys)
{
var descendants = new List<Guid>();

View File

@@ -8,8 +8,9 @@ public interface INavigationQueryService
{
bool TryGetParentKey(Guid childKey, out Guid? parentKey);
bool TryGetRootKeys(out IEnumerable<Guid> rootKeys);
bool TryGetChildrenKeys(Guid parentKey, out IEnumerable<Guid> childrenKeys);
bool TryGetRootKeys(out IEnumerable<Guid> childrenKeys);
bool TryGetDescendantsKeys(Guid parentKey, out IEnumerable<Guid> descendantsKeys);

View File

@@ -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<Guid> 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<Guid> afterCopyRootSiblingsKeys);
DocumentNavigationQueryService.TryGetChildrenKeys(sourceParentKey.Value, out IEnumerable<Guid> sourceParentChildrenKeys);
List<Guid> 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<Guid> beforeCopyGrandChild1Descendents);
DocumentNavigationQueryService.TryGetChildrenKeys(Child3.Key, out IEnumerable<Guid> 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<Guid> afterCopyChild3ChildrenKeys);
DocumentNavigationQueryService.TryGetChildrenKeys(copiedItemKey, out IEnumerable<Guid> afterCopyGrandChild1Descendents);
List<Guid> child3ChildrenList = afterCopyChild3ChildrenKeys.ToList();
List<Guid> 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);
});
}
}

View File

@@ -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<Guid> 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<Guid> 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<Guid> updatedChildrenKeys);
List<Guid> childrenList = updatedChildrenKeys.ToList();
// Assert
Assert.Multiple(() =>
{
Assert.IsNotEmpty(childrenList);
Assert.AreEqual(initialChild1ChildrenCount + 1, childrenList.Count);
Assert.IsTrue(childrenList.Contains(createdItemKey));
});
}
}

View File

@@ -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]

View File

@@ -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<Guid> 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<Guid> 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<Guid> 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);
}
});
}
}

View File

@@ -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<Guid> initialDescendantsKeys);
var beforeMoveDescendants = initialDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable<Guid> beforeMoveInitialParentChildrenKeys);
var beforeMoveInitialParentChildren = beforeMoveInitialParentChildrenKeys.ToList();
DocumentNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable<Guid> 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<Guid> afterMoveDescendantsKeys);
var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable<Guid> afterMoveInitialParentChildrenKeys);
var afterMoveInitialParentChildren = afterMoveInitialParentChildrenKeys.ToList();
DocumentNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable<Guid> 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<Guid> initialDescendantsKeys);
var beforeMoveDescendants = initialDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable<Guid> beforeMoveInitialParentDescendantsKeys);
var beforeMoveInitialParentDescendants = beforeMoveInitialParentDescendantsKeys.ToList();
// The Root node is the only node at the root
DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable<Guid> 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<Guid> afterMoveDescendantsKeys);
var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetDescendantsKeys((Guid)originalParentKey, out IEnumerable<Guid> afterMoveInitialParentDescendantsKeys);
var afterMoveInitialParentDescendants = afterMoveInitialParentDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> initialSiblingsKeys);
var beforeMoveRecycleBinSiblingsCount = initialSiblingsKeys.Count();
Assert.AreEqual(beforeMoveRecycleBinSiblingsCount, 0);
DocumentNavigationQueryService.TryGetParentKey(nodeToMoveToRecycleBin, out Guid? originalParentKey);
DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToMoveToRecycleBin, out IEnumerable<Guid> initialDescendantsKeys);
var beforeMoveDescendants = initialDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable<Guid> 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<Guid> afterMoveDescendantsKeys);
var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
DocumentNavigationQueryService.TryGetChildrenKeys((Guid)originalParentKey, out IEnumerable<Guid> afterMoveParentChildrenKeys);
var afterMoveParentSiblingsCount = afterMoveParentChildrenKeys.Count();
DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> originalChildrenKeys);
DocumentNavigationQueryService.TryGetDescendantsKeys(nodeKey, out IEnumerable<Guid> originalDescendantsKeys);
DocumentNavigationQueryService.TryGetAncestorsKeys(nodeKey, out IEnumerable<Guid> originalAncestorsKeys);
DocumentNavigationQueryService.TryGetSiblingsKeys(nodeKey, out IEnumerable<Guid> originalSiblingsKeys);
// Im-memory navigation structure is empty here
// In-memory navigation structure is empty here
var newDocumentNavigationService = new DocumentNavigationService(GetRequiredService<ICoreScopeProvider>(), GetRequiredService<INavigationRepository>());
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<Guid> originalChildrenKeys);
DocumentNavigationQueryService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable<Guid> originalDescendantsKeys);
DocumentNavigationQueryService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable<Guid> originalAncestorsKeys);
DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable<Guid> originalSiblingsKeys);
// In-memory navigation structure is empty here
var newDocumentNavigationService = new DocumentNavigationService(GetRequiredService<ICoreScopeProvider>(), GetRequiredService<INavigationRepository>());
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<Guid> childrenKeysFromRebuild);
newDocumentNavigationService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable<Guid> descendantsKeysFromRebuild);
newDocumentNavigationService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable<Guid> ancestorsKeysFromRebuild);
newDocumentNavigationService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> initialSiblingsKeys);
DocumentNavigationQueryService.TryGetDescendantsKeysInBin(nodeToRestore, out IEnumerable<Guid> 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<Guid> updatedSiblingsKeys);
DocumentNavigationQueryService.TryGetDescendantsKeys(restoredItemKey, out IEnumerable<Guid> 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);
});
}

View File

@@ -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<Guid> 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<Guid> updatedSiblingsKeys);
List<Guid> 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<Guid> 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<Guid> updatedChildrenKeys);
List<Guid> childrenList = updatedChildrenKeys.ToList();
// Assert
Assert.Multiple(() =>
{
Assert.IsNotEmpty(childrenList);
Assert.AreEqual(initialChild1ChildrenCount + 1, childrenList.Count);
Assert.IsTrue(childrenList.Contains(createdItemKey));
});
}
}

View File

@@ -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<Guid> 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);
}
});
}
}

View File

@@ -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<Guid> 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<Guid> initialSiblingsKeys);
var initialSiblingsCount = initialSiblingsKeys.Count();
Assert.AreEqual(initialSiblingsCount, 1);
// Act
await MediaEditingService.DeleteFromRecycleBinAsync(nodeToDelete, Constants.Security.SuperUserKey);
// Assert
MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable<Guid> 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);
}
});
}
}

View File

@@ -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<Guid> initialDescendantsKeys);
var beforeMoveDescendants = initialDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable<Guid> beforeMoveInitialParentDescendantsKeys);
var beforeMoveInitialParentDescendants = beforeMoveInitialParentDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable<Guid> 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<Guid> afterMoveDescendantsKeys);
var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable<Guid> afterMoveInitialParentDescendantsKeys);
var afterMoveInitialParentDescendants = afterMoveInitialParentDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable<Guid> 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<Guid> initialDescendantsKeys);
var beforeMoveDescendants = initialDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable<Guid> beforeMoveInitialParentDescendantsKeys);
var beforeMoveInitialParentDescendants = beforeMoveInitialParentDescendantsKeys.ToList();
// The Root node is the only node at the root
MediaNavigationQueryService.TryGetSiblingsKeys(Album.Key, out IEnumerable<Guid> 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<Guid> afterMoveDescendantsKeys);
var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetDescendantsKeys((Guid)originalParentKey, out IEnumerable<Guid> afterMoveInitialParentDescendantsKeys);
var afterMoveInitialParentDescendants = afterMoveInitialParentDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetSiblingsKeys(Album.Key, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> initialSiblingsKeys);
var beforeMoveRecycleBinSiblingsCount = initialSiblingsKeys.Count();
Assert.AreEqual(beforeMoveRecycleBinSiblingsCount, 0);
MediaNavigationQueryService.TryGetParentKey(nodeToMoveToRecycleBin, out Guid? originalParentKey);
MediaNavigationQueryService.TryGetDescendantsKeys(nodeToMoveToRecycleBin, out IEnumerable<Guid> initialDescendantsKeys);
var beforeMoveDescendants = initialDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable<Guid> 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<Guid> afterMoveDescendantsKeys);
var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
MediaNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable<Guid> afterMoveParentChildrenKeys);
var afterMoveParentSiblingsCount = afterMoveParentChildrenKeys.Count();
MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> originalChildrenKeys);
MediaNavigationQueryService.TryGetDescendantsKeys(nodeKey, out IEnumerable<Guid> originalDescendantsKeys);
MediaNavigationQueryService.TryGetAncestorsKeys(nodeKey, out IEnumerable<Guid> originalAncestorsKeys);
MediaNavigationQueryService.TryGetSiblingsKeys(nodeKey, out IEnumerable<Guid> originalSiblingsKeys);
// Im-memory navigation structure is empty here
// In-memory navigation structure is empty here
var newMediaNavigationService = new MediaNavigationService(GetRequiredService<ICoreScopeProvider>(), GetRequiredService<INavigationRepository>());
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<Guid> originalChildrenKeys);
MediaNavigationQueryService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable<Guid> originalDescendantsKeys);
MediaNavigationQueryService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable<Guid> originalAncestorsKeys);
MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable<Guid> originalSiblingsKeys);
// In-memory navigation structure is empty here
var newMediaNavigationService = new MediaNavigationService(GetRequiredService<ICoreScopeProvider>(), GetRequiredService<INavigationRepository>());
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<Guid> childrenKeysFromRebuild);
newMediaNavigationService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable<Guid> descendantsKeysFromRebuild);
newMediaNavigationService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable<Guid> ancestorsKeysFromRebuild);
newMediaNavigationService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> initialSiblingsKeys);
MediaNavigationQueryService.TryGetDescendantsKeysInBin(nodeToRestore, out IEnumerable<Guid> 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<Guid> updatedSiblingsKeys);
MediaNavigationQueryService.TryGetDescendantsKeys(restoredItemKey, out IEnumerable<Guid> 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);
});
}
}

View File

@@ -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<Guid> initialChildrenKeys);
MediaNavigationQueryService.TryGetDescendantsKeys(nodeToUpdate, out IEnumerable<Guid> initialDescendantsKeys);
MediaNavigationQueryService.TryGetAncestorsKeys(nodeToUpdate, out IEnumerable<Guid> initialAncestorsKeys);
MediaNavigationQueryService.TryGetSiblingsKeys(nodeToUpdate, out IEnumerable<Guid> 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<Guid> childrenKeysAfterUpdate);
MediaNavigationQueryService.TryGetDescendantsKeys(updatedItemKey, out IEnumerable<Guid> descendantsKeysAfterUpdate);
MediaNavigationQueryService.TryGetAncestorsKeys(updatedItemKey, out IEnumerable<Guid> ancestorsKeysAfterUpdate);
MediaNavigationQueryService.TryGetSiblingsKeys(updatedItemKey, out IEnumerable<Guid> 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);
});
}
}