public void ShouldRebasePathOfChildren()
		{
			var root = new FakeItem(children: new[] { new FakeItem() }, id: Guid.NewGuid());

			var rebase = new PathRebasingProxyItem(root, "/sitecore/new", Guid.NewGuid());

			rebase.GetChildren().First().Path.Should().Be("/sitecore/new/test item/test item");
			rebase.GetChildren().First().ParentId.Should().Be(root.Id);
		}
		public void ShouldRebasePath()
		{
			var root = new FakeItem();
			var pid = Guid.NewGuid();

			var rebase = new PathRebasingProxyItem(root, "/sitecore/new", pid);

			rebase.Path.Should().Be("/sitecore/new/test item");
			rebase.ParentId.Should().Be(pid);
		}
        public virtual void MoveItem(ItemDefinition itemDefinition, ItemDefinition destination, CallContext context)
        {
            if (DisableSerialization) return;

            Assert.ArgumentNotNull(itemDefinition, "itemDefinition");

            var sourceItem = GetSourceFromId(itemDefinition.ID, true); // we use cache here because we want the old path (no cache would have the new path); TpSync always has old path

            var oldPath = sourceItem.Path; // NOTE: we cap the path here, because Sitecore can change the item's path value as we're updating stuff.

            var destinationItem = GetSourceFromId(destination.ID);

            if (destinationItem == null) return; // can occur with TpSync on, when this isn't the configuration we're moving for the data store will return null

            if (!_predicate.Includes(destinationItem).IsIncluded) // if the destination we are moving to is NOT included for serialization, we delete the existing item
            {
                var existingItem = _targetDataStore.GetByPathAndId(sourceItem.Path, sourceItem.Id, sourceItem.DatabaseName);

                if (existingItem != null)
                {
                    _targetDataStore.Remove(existingItem);
                    _logger.MovedItemToNonIncludedLocation(_targetDataStore.FriendlyName, existingItem);
                }

                return;
            }

            // rebase the path to the new destination path (this handles children too)
            var rebasedSourceItem = new PathRebasingProxyItem(sourceItem, destinationItem.Path, destinationItem.Id);

            // this allows us to filter out any excluded children by predicate when the data store moves children
            var predicatedItem = new PredicateFilteredItemData(rebasedSourceItem, _predicate);

            _targetDataStore.MoveOrRenameItem(predicatedItem, oldPath);
            _logger.MovedItem(_targetDataStore.FriendlyName, predicatedItem, destinationItem);
        }
		/*
		Moving an item involves:
		- Get the item from the tree, delete
		- If the new path is included in ANY tree
			- Get the serialized parent at the destination
			- Get the moved tree from Sitecore, save whole tree (NOTE: we had to update to final path in DP - what about children are those with old or new path?)

		Renaming an item involves:
		- The same thing as moving an item
		*/
		public virtual void MoveOrRenameItem(IItemData itemWithFinalPath, string oldPath)
		{
			// GET EXISTING ITEM WE'RE MOVING + DESCENDANT PATHS
			var oldPathTree = GetTreeForPath(oldPath, itemWithFinalPath.DatabaseName);
			Dictionary<string, IItemMetadata> oldPathItemAndDescendants;

			var oldPathItem = oldPathTree?.GetItemsByPath(oldPath).FirstOrDefault(item => item.Id == itemWithFinalPath.Id);
			if (oldPathItem != null)
			{
				oldPathItemAndDescendants = oldPathTree.GetDescendants(oldPathItem, false).ToDictionary(item => item.SerializedItemId);
				oldPathItemAndDescendants.Add(oldPathItem.SerializedItemId, oldPathItem);
			}
			else oldPathItemAndDescendants = new Dictionary<string, IItemMetadata>();

			// WRITE THE NEW MOVED/RENAMED ITEMS TO THE TREE (note: delete goes last because with TpSync we need the old items to read from)
			var newPathTree = GetTreeForPath(itemWithFinalPath.Path, itemWithFinalPath.DatabaseName);

			// force consistency of parent IDs and paths among child items before we serialize them
			var rebasedPathItem = new PathRebasingProxyItem(itemWithFinalPath);

			// add new tree, if it's included (if it's moving to a non included path we simply delete it and are done)
			if (newPathTree != null)
			{
				var saveQueue = new Queue<IItemData>();
				saveQueue.Enqueue(rebasedPathItem);

				while (saveQueue.Count > 0)
				{
					var parent = saveQueue.Dequeue();

					var tree = GetTreeForPath(parent.Path, parent.DatabaseName);

					if (tree == null) throw new InvalidOperationException("No trees contained the global path " + parent.Path);

					var savedPath = tree.Save(parent);

					// if we saved an item that was a former child of the item we want to keep it when we're doing deletions
					if (oldPathItemAndDescendants.ContainsKey(savedPath)) oldPathItemAndDescendants.Remove(savedPath);

					var children = parent.GetChildren();

					foreach (var child in children)
					{
						saveQueue.Enqueue(child);
					}
				}
			}

			// in case an item was renamed by case or someone calls rename without renaming, we don't want to delete anything
			// 'cause that'd just delete the item, not move it :)
			if (oldPath.Equals(itemWithFinalPath.Path, StringComparison.OrdinalIgnoreCase)) return;

			// REMOVE EXISTING ITEMS (if any)
			// (excluding any items that we wrote to during the save phase above, e.g. loopback path items may not change during a move)
			if (oldPathTree != null)
			{
				var oldItems = oldPathItemAndDescendants
					.Select(key => key.Value)
					.OrderByDescending(item => item.Path)
					.ToArray();

				foreach (var item in oldItems) oldPathTree.RemoveWithoutChildren(item);
			}
		}