From 2c795662d2d5493039eda2a85120c2e3d39b3758 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 24 Jul 2019 18:46:14 +1000 Subject: [PATCH] Adds tests for PublishedContentQuery search --- .../TestHelpers/RandomIdRamDirectory.cs | 22 +++ src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + .../Web/PublishedContentQueryTests.cs | 157 ++++++++++++++++++ src/Umbraco.Tests/Web/UmbracoHelperTests.cs | 10 +- src/Umbraco.Web/IPublishedContentQuery.cs | 12 +- src/Umbraco.Web/PublishedContentQuery.cs | 17 +- 6 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 src/Umbraco.Tests/TestHelpers/RandomIdRamDirectory.cs create mode 100644 src/Umbraco.Tests/Web/PublishedContentQueryTests.cs diff --git a/src/Umbraco.Tests/TestHelpers/RandomIdRamDirectory.cs b/src/Umbraco.Tests/TestHelpers/RandomIdRamDirectory.cs new file mode 100644 index 0000000000..34904db1ae --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/RandomIdRamDirectory.cs @@ -0,0 +1,22 @@ +using Lucene.Net.Store; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Tests.TestHelpers +{ + + /// + /// Used for tests with Lucene so that each RAM directory is unique + /// + public class RandomIdRAMDirectory : RAMDirectory + { + private readonly string _lockId = Guid.NewGuid().ToString(); + public override string GetLockId() + { + return _lockId; + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f41ff1dd07..717006b702 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -157,6 +157,7 @@ + @@ -268,6 +269,7 @@ + diff --git a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs b/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs new file mode 100644 index 0000000000..b2a2741bcf --- /dev/null +++ b/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Examine; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Store; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Examine; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.Web +{ + [TestFixture] + public class PublishedContentQueryTests + { + + private class TestIndex : LuceneIndex, IUmbracoIndex + { + private readonly string[] _fieldNames; + + public TestIndex(string name, Directory luceneDirectory, string[] fieldNames) + : base(name, luceneDirectory, null, null, null, null) + { + _fieldNames = fieldNames; + } + public bool EnableDefaultEventHandler => throw new NotImplementedException(); + public bool PublishedValuesOnly => throw new NotImplementedException(); + public IEnumerable GetFields() => _fieldNames; + } + + private TestIndex CreateTestIndex(Directory luceneDirectory, string[] fieldNames) + { + var indexer = new TestIndex("TestIndex", luceneDirectory, fieldNames); + + //populate with some test data + indexer.IndexItem(new ValueSet("1", "content", new Dictionary + { + [fieldNames[0]] = "Hello world, there are products here", + [UmbracoContentIndex.VariesByCultureFieldName] = "n" + })); + indexer.IndexItem(new ValueSet("2", "content", new Dictionary + { + [fieldNames[1]] = "Hello world, there are products here", + [UmbracoContentIndex.VariesByCultureFieldName] = "y" + })); + indexer.IndexItem(new ValueSet("3", "content", new Dictionary + { + [fieldNames[2]] = "Hello world, there are products here", + [UmbracoContentIndex.VariesByCultureFieldName] = "y" + })); + return indexer; + } + + private PublishedContentQuery CreatePublishedContentQuery(IIndex indexer) + { + var examineManager = new Mock(); + IIndex outarg = indexer; + examineManager.Setup(x => x.TryGetIndex("TestIndex", out outarg)).Returns(true); + + var contentCache = new Mock(); + contentCache.Setup(x => x.GetById(It.IsAny())).Returns((int intId) => Mock.Of(x => x.Id == intId)); + var snapshot = Mock.Of(x => x.Content == contentCache.Object); + var variationContext = new VariationContext(); + var variationContextAccessor = Mock.Of(x => x.VariationContext == variationContext); + + return new PublishedContentQuery(snapshot, variationContextAccessor, examineManager.Object); + } + + [Test] + public void Search_Wildcard() + { + using (var luceneDir = new RandomIdRAMDirectory()) + { + var fieldNames = new[] { "title", "title_en-us", "title_fr-fr" }; + using (var indexer = CreateTestIndex(luceneDir, fieldNames)) + { + var pcq = CreatePublishedContentQuery(indexer); + + var results = pcq.Search("Products", "*", "TestIndex"); + + var ids = results.Select(x => x.Content.Id).ToList(); + Assert.AreEqual(3, ids.Count); + + //returns results for all fields and document types + Assert.IsTrue(ids.Contains(1) && ids.Contains(2) && ids.Contains(3)); + } + } + } + + [Test] + public void Search_Invariant() + { + using (var luceneDir = new RandomIdRAMDirectory()) + { + var fieldNames = new[] { "title", "title_en-us", "title_fr-fr" }; + using (var indexer = CreateTestIndex(luceneDir, fieldNames)) + { + var pcq = CreatePublishedContentQuery(indexer); + + var results = pcq.Search("Products", null, "TestIndex"); + + var ids = results.Select(x => x.Content.Id).ToList(); + Assert.AreEqual(1, ids.Count); + + //returns results for only invariant fields and invariant documents + Assert.IsTrue(ids.Contains(1) && !ids.Contains(2) && !ids.Contains(3)); + } + } + } + + [Test] + public void Search_Culture1() + { + using (var luceneDir = new RandomIdRAMDirectory()) + { + var fieldNames = new[] { "title", "title_en-us", "title_fr-fr" }; + using (var indexer = CreateTestIndex(luceneDir, fieldNames)) + { + var pcq = CreatePublishedContentQuery(indexer); + + var results = pcq.Search("Products", "en-us", "TestIndex"); + + var ids = results.Select(x => x.Content.Id).ToList(); + Assert.AreEqual(2, ids.Count); + + //returns results for en-us fields and invariant fields for all document types + Assert.IsTrue(ids.Contains(1) && ids.Contains(2) && !ids.Contains(3)); + } + } + } + + [Test] + public void Search_Culture2() + { + using (var luceneDir = new RandomIdRAMDirectory()) + { + var fieldNames = new[] { "title", "title_en-us", "title_fr-fr" }; + using (var indexer = CreateTestIndex(luceneDir, fieldNames)) + { + var pcq = CreatePublishedContentQuery(indexer); + + var results = pcq.Search("Products", "fr-fr", "TestIndex"); + + var ids = results.Select(x => x.Content.Id).ToList(); + Assert.AreEqual(2, ids.Count); + + //returns results for fr-fr fields and invariant fields for all document types + Assert.IsTrue(ids.Contains(1) && !ids.Contains(2) && ids.Contains(3)); + } + } + } + } +} diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index b23b5bd6b7..26d85f60cf 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -1,5 +1,8 @@ using System; using System.Text; +using Examine.LuceneEngine; +using Lucene.Net.Analysis; +using Lucene.Net.Analysis.Standard; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -13,18 +16,19 @@ using Umbraco.Web; namespace Umbraco.Tests.Web { + [TestFixture] public class UmbracoHelperTests - { + { [TearDown] public void TearDown() { Current.Reset(); } - - + + // ------- Int32 conversion tests [Test] public static void Converting_Boxed_34_To_An_Int_Returns_34() diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index 35db121a60..8a8d678aba 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -39,7 +39,11 @@ namespace Umbraco.Web /// Optional culture. /// Optional index name. /// - /// When the is not specified or is *, all cultures are searched. To search only invariant use null. + /// + /// When the is not specified or is *, all cultures are searched. + /// To search for only invariant documents and fields use null. + /// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents. + /// /// While enumerating results, the ambient culture is changed to be the searched culture. /// IEnumerable Search(string term, string culture = "*", string indexName = null); @@ -54,7 +58,11 @@ namespace Umbraco.Web /// Optional culture. /// Optional index name. /// - /// When the is not specified or is *, all cultures are searched. To search only invariant use null. + /// + /// When the is not specified or is *, all cultures are searched. + /// To search for only invariant documents and fields use null. + /// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents. + /// /// While enumerating results, the ambient culture is changed to be the searched culture. /// IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null); diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index cfdd31f138..2dbe4de4c5 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -20,14 +20,22 @@ namespace Umbraco.Web { private readonly IPublishedSnapshot _publishedSnapshot; private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IExamineManager _examineManager; + + [Obsolete("Use the constructor with all parameters instead")] + public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor) + : this (publishedSnapshot, variationContextAccessor, ExamineManager.Instance) + { + } /// /// Initializes a new instance of the class. /// - public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor) + public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, IExamineManager examineManager) { _publishedSnapshot = publishedSnapshot ?? throw new ArgumentNullException(nameof(publishedSnapshot)); _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); + _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); } #region Content @@ -187,7 +195,7 @@ namespace Umbraco.Web ? Constants.UmbracoIndexes.ExternalIndexName : indexName; - if (!ExamineManager.Instance.TryGetIndex(indexName, out var index) || !(index is IUmbracoIndex umbIndex)) + if (!_examineManager.TryGetIndex(indexName, out var index) || !(index is IUmbracoIndex umbIndex)) throw new InvalidOperationException($"No index found by name {indexName} or is not of type {typeof(IUmbracoIndex)}"); var searcher = umbIndex.GetSearcher(); @@ -216,8 +224,7 @@ namespace Umbraco.Web //get all index fields suffixed with the culture name supplied var cultureFields = umbIndex.GetCultureAndInvariantFields(culture).ToArray(); - var qry = searcher.CreateQuery(); - qry = qry.ManagedQuery(term, cultureFields); + var qry = searcher.CreateQuery().ManagedQuery(term, cultureFields); results = qry.Execute(count); } @@ -313,7 +320,7 @@ namespace Umbraco.Web } } - + #endregion