diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 6a5e7dcaac..ef1538b401 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -270,6 +270,7 @@
+
True
diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs
new file mode 100644
index 0000000000..71a131318f
--- /dev/null
+++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Linq;
+using Examine;
+using Examine.LuceneEngine;
+using Examine.LuceneEngine.Providers;
+using Examine.LuceneEngine.SearchCriteria;
+using Examine.SearchCriteria;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Store;
+using NUnit.Framework;
+using Umbraco.Tests.PartialTrust;
+using UmbracoExamine;
+
+namespace Umbraco.Tests.UmbracoExamine
+{
+
+ ///
+ /// Tests the standard indexing capabilities
+ ///
+ [TestFixture, RequiresSTA]
+ public class IndexTest : AbstractPartialTrustFixture
+ {
+
+ /////
+ ///
+ /// Check that the node signalled as protected in the content service is not present in the index.
+ ///
+ [Test]
+ public void Index_Protected_Content_Not_Indexed()
+ {
+
+ var protectedQuery = new BooleanQuery();
+ protectedQuery.Add(
+ new BooleanClause(
+ new TermQuery(new Term(UmbracoContentIndexer.IndexTypeFieldName, IndexTypes.Content)),
+ BooleanClause.Occur.MUST));
+
+ protectedQuery.Add(
+ new BooleanClause(
+ new TermQuery(new Term(UmbracoContentIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())),
+ BooleanClause.Occur.MUST));
+
+ var collector = new AllHitsCollector(false, true);
+ var s = _searcher.GetSearcher();
+ s.Search(protectedQuery, collector);
+
+ Assert.AreEqual(0, collector.Count, "Protected node should not be indexed");
+
+ }
+
+ [Test]
+ public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID()
+ {
+ //change parent id to 1116
+ var existingCriteria = ((IndexCriteria)_indexer.IndexerData);
+ _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
+ 1116);
+
+ //rebuild so it excludes children unless they are under 1116
+ _indexer.RebuildIndex();
+
+ //ensure that node 2112 doesn't exist
+ var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
+ Assert.AreEqual(0, results.Count());
+
+ //get a node from the data repo (this one exists underneath 2222)
+ var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
+ .Root
+ .Elements()
+ .Where(x => (int)x.Attribute("id") == 2112)
+ .First();
+
+ var currPath = (string)node.Attribute("path"); //should be : -1,2222,2112
+ Assert.AreEqual("-1,2222,2112", currPath);
+
+ //now mimic moving 2112 to 1116
+ node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
+ node.SetAttributeValue("parentID", "1116");
+
+ //now reindex the node, this should first delete it and then WILL add it because of the parent id constraint
+ _indexer.ReIndexNode(node, IndexTypes.Media);
+
+ //RESET the parent id
+ existingCriteria = ((IndexCriteria)_indexer.IndexerData);
+ _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
+ null);
+
+ //now ensure it's deleted
+ var newResults = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
+ Assert.AreEqual(1, newResults.Count());
+
+ }
+
+ [Test]
+ public void Index_Move_Media_To_Non_Indexable_ParentID()
+ {
+ //get a node from the data repo (this one exists underneath 2222)
+ var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
+ .Root
+ .Elements()
+ .Where(x => (int)x.Attribute("id") == 2112)
+ .First();
+
+ var currPath = (string)node.Attribute("path"); //should be : -1,2222,2112
+ Assert.AreEqual("-1,2222,2112", currPath);
+
+ //ensure it's indexed
+ _indexer.ReIndexNode(node, IndexTypes.Media);
+
+ //change the parent node id to be the one it used to exist under
+ var existingCriteria = ((IndexCriteria)_indexer.IndexerData);
+ _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
+ 2222);
+
+ //now mimic moving the node underneath 1116 instead of 2222
+ node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
+ node.SetAttributeValue("parentID", "1116");
+
+ //now reindex the node, this should first delete it and then NOT add it because of the parent id constraint
+ _indexer.ReIndexNode(node, IndexTypes.Media);
+
+ //RESET the parent id
+ existingCriteria = ((IndexCriteria)_indexer.IndexerData);
+ _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
+ null);
+
+ //now ensure it's deleted
+ var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
+ Assert.AreEqual(0, results.Count());
+
+ }
+
+
+ ///
+ /// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly.
+ /// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly.
+ ///
+ [Test]
+ public void Index_Reindex_Content()
+ {
+ var s = (IndexSearcher)_searcher.GetSearcher();
+
+ //first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine!
+ var r = IndexReader.Open(s.GetIndexReader().Directory(), false);
+ var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content);
+ var delCount = r.DeleteDocuments(contentTerm);
+ r.Commit();
+ r.Close();
+
+ //make sure the content is gone. This is done with lucene APIs, not examine!
+ var collector = new AllHitsCollector(false, true);
+ var query = new TermQuery(contentTerm);
+ s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date.
+ s.Search(query, collector);
+ Assert.AreEqual(0, collector.Count);
+
+ //call our indexing methods
+ _indexer.IndexAll(IndexTypes.Content);
+
+ collector = new AllHitsCollector(false, true);
+ s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date.
+ s.Search(query, collector);
+ Assert.AreEqual(9, collector.Count);
+ }
+
+ ///
+ /// This will delete an item from the index and ensure that all children of the node are deleted too!
+ ///
+ [Test]
+ public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
+ {
+
+ //now delete a node that has children
+
+ _indexer.DeleteFromIndex(1140.ToString());
+ //this node had children: 1141 & 1142, let's ensure they are also removed
+
+ var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1141).Compile());
+ Assert.AreEqual(0, results.Count());
+
+ results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1142).Compile());
+ Assert.AreEqual(0, results.Count());
+
+ }
+
+ #region Private methods and properties
+
+ private readonly TestContentService _contentService = new TestContentService();
+ private readonly TestMediaService _mediaService = new TestMediaService();
+
+ private static UmbracoExamineSearcher _searcher;
+ private static UmbracoContentIndexer _indexer;
+
+ #endregion
+
+ #region Initialize and Cleanup
+
+ private Lucene.Net.Store.Directory _luceneDir;
+
+ public override void TestTearDown()
+ {
+ _luceneDir.Dispose();
+ }
+
+ public override void TestSetup()
+ {
+ _luceneDir = new RAMDirectory();
+ _indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir);
+ _indexer.RebuildIndex();
+ _searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir);
+ }
+
+
+ #endregion
+ }
+}
\ No newline at end of file