// Copyright (c) Umbraco. // See LICENSE for more details. #pragma warning disable SA1124 // Do not use regions (justification: regions are currently adding some useful organisation to this file) using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Sync; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { [TestFixture] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class ContentEventsTests : UmbracoIntegrationTestWithContent { private CacheRefresherCollection CacheRefresherCollection => GetRequiredService(); private IUmbracoContextFactory UmbracoContextFactory => GetRequiredService(); private ILogger Logger => GetRequiredService>(); #region Setup private class TestNotificationHandler : INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler { private readonly IDocumentRepository _documentRepository; public TestNotificationHandler(IDocumentRepository documentRepository) { _documentRepository = documentRepository; } public void Handle(ContentCacheRefresherNotification args) { // reports the event as: "ContentCache/,.../X // where // is(are) the action(s) // X is the event content ID if (args.MessageType != MessageType.RefreshByPayload) { throw new NotSupportedException(); } // We're in between tests, don't do anything. if (_events is null) { return; } foreach (ContentCacheRefresher.JsonPayload payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) { var e = new EventInstance { Message = _msgCount, Sender = "ContentCacheRefresher", EventArgs = payload, Name = payload.ChangeTypes.ToString().Replace(" ", string.Empty), Args = payload.Id.ToInvariantString() }; _events.Add(e); } _msgCount++; } public void Handle(ContentDeletedNotification notification) { // reports the event as : "ContentRepository/Remove/X" // where // X is the event content ID var e = new EventInstance { Message = _msgCount++, Sender = "ContentRepository", EventArgs = null, // Notification has no event args Name = "Remove", Args = string.Join(",", notification.DeletedEntities.Select(x => x.Id)) }; _events.Add(e); } public void Handle(ContentDeletingVersionsNotification notification) { // reports the event as : "ContentRepository/Remove/X:Y" // where // X is the event content ID // Y is the event content version GUID var e = new EventInstance { Message = _msgCount++, Sender = "ContentRepository", EventArgs = null, // Notification has no args Name = "RemoveVersion", Args = $"{notification.Id}:{notification.SpecificVersion}" }; _events.Add(e); } public void Handle(ContentRefreshNotification notification) { // reports the event as : "ContentRepository/Refresh/XY-Z" // where // X can be u (unpublished) or p (published) and is the state of the event content // Y can be u (unchanged), x (unpublishing), p (published) or m (masked) and is the state of the published version // Z is the event content ID // reports the event as "ContentRepository/Refresh/id.xyz // where // id is the event content identifier // x is u|p and is the (un)published state of the event content // y is +|-|= and is the action (publish, unpublish, no change) // z is u|p|m and is the (un)published state after the event if (_events is null) { return; } IContent[] entities = new[] { notification.Entity }; // args.Entities var e = new EventInstance { Message = _msgCount++, Sender = "ContentRepository", Name = "Refresh", Args = string.Join(",", entities.Select(x => { PublishedState publishedState = ((Content)x).PublishedState; string xstate = x.Published ? "p" : "u"; if (publishedState == PublishedState.Publishing) { xstate += "+" + (x.ParentId == -1 || _documentRepository.IsPathPublished(_documentRepository.Get(x.ParentId)) ? "p" : "m"); } else if (publishedState == PublishedState.Unpublishing) { xstate += "-u"; } else { xstate += "=" + (x.Published ? (_documentRepository.IsPathPublished(x) ? "p" : "m") : "u"); } return $"{x.Id}.{xstate}"; })) }; _events.Add(e); } } protected override void CustomTestSetup(IUmbracoBuilder builder) { builder.AddNuCache(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() ; builder.AddNotificationHandler(); } [SetUp] public void SetUp() { _events = new List(); // prepare content type Template template = TemplateBuilder.CreateTextPageTemplate(); FileService.SaveTemplate(template); _contentType = ContentTypeBuilder.CreateSimpleContentType("whatever", "Whatever", defaultTemplateId: template.Id); _contentType.Key = Guid.NewGuid(); FileService.SaveTemplate(_contentType.DefaultTemplate); ContentTypeService.Save(_contentType); } private static IList _events; private static int _msgCount; private IContentType _contentType; private void ResetEvents() { _events = new List(); _msgCount = 0; Logger.LogDebug("RESET EVENTS"); } private IContent CreateContent(int parentId = -1) { Content content1 = ContentBuilder.CreateSimpleContent(_contentType, "Content1", parentId); ContentService.Save(content1); return content1; } private IContent CreateBranch() { Content content1 = ContentBuilder.CreateSimpleContent(_contentType, "Content1"); ContentService.SaveAndPublish(content1); // 2 (published) // .1 (published) // .2 (not published) Content content2 = ContentBuilder.CreateSimpleContent(_contentType, "Content2", content1); ContentService.SaveAndPublish(content2); Content content21 = ContentBuilder.CreateSimpleContent(_contentType, "Content21", content2); ContentService.SaveAndPublish(content21); Content content22 = ContentBuilder.CreateSimpleContent(_contentType, "Content22", content2); ContentService.Save(content22); // 3 (not published) // .1 (not published) // .2 (not published) Content content3 = ContentBuilder.CreateSimpleContent(_contentType, "Content3", content1); ContentService.Save(content3); Content content31 = ContentBuilder.CreateSimpleContent(_contentType, "Content31", content3); ContentService.Save(content31); Content content32 = ContentBuilder.CreateSimpleContent(_contentType, "Content32", content3); ContentService.Save(content32); // 4 (published + saved) // .1 (published) // .2 (not published) Content content4 = ContentBuilder.CreateSimpleContent(_contentType, "Content4", content1); ContentService.SaveAndPublish(content4); content4.Name = "Content4X"; ContentService.Save(content4); Content content41 = ContentBuilder.CreateSimpleContent(_contentType, "Content41", content4); ContentService.SaveAndPublish(content41); Content content42 = ContentBuilder.CreateSimpleContent(_contentType, "Content42", content4); ContentService.Save(content42); // 5 (not published) // .1 (published) // .2 (not published) Content content5 = ContentBuilder.CreateSimpleContent(_contentType, "Content5", content1); ContentService.SaveAndPublish(content5); Content content51 = ContentBuilder.CreateSimpleContent(_contentType, "Content51", content5); ContentService.SaveAndPublish(content51); Content content52 = ContentBuilder.CreateSimpleContent(_contentType, "Content52", content5); ContentService.Save(content52); ContentService.Unpublish(content5); return content1; } #endregion #region Validate Setup [Test] public void CreatedBranchIsOk() { IContent content1 = CreateBranch(); IContent[] children1 = Children(content1).ToArray(); IContent content2 = children1[0]; IContent[] children2 = Children(content2).ToArray(); IContent content21 = children2[0]; IContent content22 = children2[1]; IContent content3 = children1[1]; IContent[] children3 = Children(content3).ToArray(); IContent content31 = children3[0]; IContent content32 = children3[1]; IContent content4 = children1[2]; IContent[] children4 = Children(content4).ToArray(); IContent content41 = children4[0]; IContent content42 = children4[1]; IContent content5 = children1[3]; IContent[] children5 = Children(content5).ToArray(); IContent content51 = children5[0]; IContent content52 = children5[1]; Assert.IsTrue(content1.Published); Assert.IsFalse(content1.Edited); Assert.IsTrue(content2.Published); Assert.IsFalse(content2.Edited); Assert.IsTrue(content21.Published); Assert.IsFalse(content21.Edited); Assert.IsFalse(content22.Published); Assert.IsTrue(content22.Edited); Assert.IsFalse(content3.Published); Assert.IsTrue(content3.Edited); Assert.IsFalse(content31.Published); Assert.IsTrue(content31.Edited); Assert.IsFalse(content32.Published); Assert.IsTrue(content32.Edited); Assert.IsTrue(content4.Published); Assert.IsTrue(content4.Edited); Assert.IsTrue(content41.Published); Assert.IsFalse(content41.Edited); Assert.IsFalse(content42.Published); Assert.IsTrue(content42.Edited); Assert.IsFalse(content5.Published); Assert.IsTrue(content5.Edited); Assert.IsTrue(content51.Published); Assert.IsFalse(content51.Edited); Assert.IsFalse(content52.Published); Assert.IsTrue(content52.Edited); } #endregion #region Events tracer private class EventInstance { public int Message { get; set; } public string Sender { get; set; } public string Name { get; set; } public string Args { get; set; } public object EventArgs { get; set; } public override string ToString() => $"{Message:000}: {Sender.Replace(" ", string.Empty)}/{Name}/{Args}"; } private static readonly string[] s_propertiesImpactingAllVersions = { "SortOrder", "ParentId", "Level", "Path", "Trashed" }; private static bool HasChangesImpactingAllVersions(IContent icontent) { var content = (Content)icontent; // UpdateDate will be dirty // Published may be dirty if saving a Published entity // so cannot do this (would always be true): ////return content.IsEntityDirty(); // have to be more precise & specify properties return s_propertiesImpactingAllVersions.Any(content.IsPropertyDirty); } private void WriteEvents() { Console.WriteLine("EVENTS"); foreach (EventInstance e in _events) { Console.WriteLine(e); } } #endregion #region Utils private IEnumerable Children(IContent content) => ContentService.GetPagedChildren(content.Id, 0, int.MaxValue, out long total); #endregion #region Save, Publish & Unpublish single content [Test] public void SaveUnpublishedContent() { // rule: when a content is saved, // - repository : refresh u=u // - content cache : refresh newest IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ResetEvents(); content.Name = "changed"; ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); } [Test] public void SavePublishedContent_ContentProperty1() { // rule: when a content is saved, // - repository : refresh (u) // - content cache :: refresh newest IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); content.Name = "changed"; ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); ResetEvents(); content.Name = "again"; ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); i = 0; m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); } [Test] public void SavePublishedContent_ContentProperty2() { // rule: when a content is saved, // - repository : refresh (u) // - content cache :: refresh newest IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); content.SortOrder = 666; ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); ResetEvents(); content.SortOrder = 667; ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); i = 0; m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); } [Test] public void SavePublishedContent_UserProperty() { // rule: when a content is saved, // - repository : refresh (u) // - content cache :: refresh newest IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); content.Properties.First().SetValue("changed"); ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); ResetEvents(); content.Properties.First().SetValue("again"); ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); i = 0; m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); } [Test] public void SaveAndPublishUnpublishedContent() { // rule: when a content is saved&published, // - repository : refresh (p) // - content cache :: refresh published, newest IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ResetEvents(); content.Name = "changed"; ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.u+p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i++].ToString()); } [Test] public void SaveAndPublishPublishedContent() { // rule: when a content is saved&published, // - repository : refresh (p) // - content cache :: refresh published, newest IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); content.Name = "changed"; ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p+p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i++].ToString()); } [Test] public void PublishUnpublishedContent() { // rule: when a content is published, // - repository : refresh (p) // - published page cache :: refresh // note: whenever the published cache is refreshed, subscribers must // assume that the unpublished cache is also refreshed, with the same // values, and deal with it. IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ResetEvents(); content.Name = "changed"; ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.u+p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i++].ToString()); } [Test] public void UnpublishContent() { // rule: when a content is unpublished, // - repository : refresh (u) // - content cache :: refresh newest, remove published IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); ContentService.Unpublish(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p-u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } [Test] public void UnpublishContentWithChanges() { // rule: when a content is unpublished, // - repository : refresh (u) // - content cache :: refresh newest, remove published IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); content.Name = "changed"; ContentService.Save(content); ResetEvents(); ContentService.Unpublish(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p-u", _events[i++].ToString()); m++; ////Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/Refresh/{1}", m, content.Id), _events[i++].ToString()); ////Assert.AreEqual("changed", ContentService.GetById(((ContentCacheRefresher.JsonPayload)_events[i - 1].EventArgs).Id).Name); Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } #endregion #region Publish & Unpublish branch [Test] public void UnpublishContentBranch() { // rule: when a content branch is unpublished, // - repository :: refresh root (u) // - unpublished page cache :: refresh root // - published page cache :: remove root // note: subscribers must take care of the hierarchy and unpublish // the whole branch by themselves. Examine does it in UmbracoContentIndexer, // content caches have to do it too... wondering whether we should instead // trigger RemovePublished for all of the removed content? IContent content1 = CreateBranch(); ResetEvents(); ContentService.Unpublish(content1); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p-u", _events[i++].ToString()); m++; ////Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/Refresh/{1}", m, content1.Id), _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void PublishContentBranch() { // rule: when a content branch is published, // - repository :: refresh root (p) // - published page cache :: refresh root & descendants, database (level, sortOrder) order IContent content1 = CreateBranch(); ContentService.Unpublish(content1); ResetEvents(); ContentService.SaveAndPublish(content1); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.u+p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); // repub content1 /* var content1C = content1.Children().ToArray(); Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/RefreshPublished/{1}", m, content1C[0].Id), _events[i++].ToString()); // repub content1.content2 Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/RefreshPublished/{1}", m, content1C[2].Id), _events[i++].ToString()); // repub content1.content4 var c = ContentService.GetPublishedVersion(((ContentCacheRefresher.JsonPayload)_events[i - 1].EventArgs).Id); Assert.IsTrue(c.Published); // get the published one Assert.AreEqual("Content4", c.Name); // published has old name var content2C = content1C[0].Children().ToArray(); Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/RefreshPublished/{1}", m, content2C[0].Id), _events[i++].ToString()); // repub content1.content2.content21 var content4C = content1C[2].Children().ToArray(); Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/RefreshPublished/{1}", m, content4C[0].Id), _events[i].ToString()); // repub content1.content4.content41 */ } [Test] public void PublishContentBranchWithPublishedChildren() { // rule? IContent content1 = CreateBranch(); ContentService.Unpublish(content1); // branch is: ResetEvents(); ContentService.SaveAndPublishBranch(content1, force: false); // force = false, don't publish unpublished items foreach (EventInstance e in _events) { Console.WriteLine(e); } Assert.AreEqual(3, _msgCount); Assert.AreEqual(3, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); // force:false => only republish the root node + nodes that are edited #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.u+p", _events[i++].ToString()); // content1 was unpublished, now published // change: only content4 shows here, because it has changes - others don't need to be published ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p+p", _events[i++].ToString()); // content1/content2 Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p+p", _events[i++].ToString()); // content1/content4 ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p+p", _events[i++].ToString()); // content1/content2/content21 ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p+p", _events[i++].ToString()); // content1/content4/content41 Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); // repub content1 #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void PublishContentBranchWithAllChildren() { // rule? IContent content1 = CreateBranch(); ContentService.Unpublish(content1); ResetEvents(); ContentService.SaveAndPublishBranch(content1, force: true); // force = true, also publish unpublished items foreach (EventInstance e in _events) { Console.WriteLine(e); } Assert.AreEqual(10, _msgCount); Assert.AreEqual(10, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. // force:true => all nodes are republished, refreshing all nodes - but only with changes - published w/out changes are not repub Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.u+p", _events[i++].ToString()); ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p+p", _events[i++].ToString()); ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p+p", _events[i++].ToString()); ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u+p", _events[i++].ToString()); ////Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[1].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); // repub content1 #pragma warning restore SA1003 // Symbols should be spaced correctly } #endregion #region Sort [Test] public void SortAll() { // rule: ? IContent content1 = CreateBranch(); IContent[] content1C = Children(content1).ToArray(); Assert.AreEqual(4, content1C.Length); IContent[] content1Csorted = new[] { content1C[3], content1C[0], content1C[1], content1C[2] }; ResetEvents(); ContentService.Sort(content1Csorted); IContent[] content1Cagain = Children(content1).ToArray(); Assert.AreEqual(4, content1Cagain.Length); Assert.AreEqual(content1C[0].Id, content1Cagain[1].Id); Assert.AreEqual(content1C[1].Id, content1Cagain[2].Id); Assert.AreEqual(content1C[2].Id, content1Cagain[3].Id); Assert.AreEqual(content1C[3].Id, content1Cagain[0].Id); Assert.AreEqual(5, _msgCount); Assert.AreEqual(8, _events.Count); int i = 0; int m = 0; #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); // content5 is not published Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); // content2 is published Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); // content3 is not published Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); // content4 is published + changes m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content1C[3].Id}", _events[i++].ToString()); // content5 is not published Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content1C[0].Id}", _events[i++].ToString()); // content2 is published Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content1C[1].Id}", _events[i++].ToString()); // content3 is not published Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content1C[2].Id}", _events[i].ToString()); // content4 is published #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void SortSome() { // rule: ? IContent content1 = CreateBranch(); IContent[] content1C = Children(content1).ToArray(); Assert.AreEqual(4, content1C.Length); IContent[] content1Csorted = new[] { content1C[0], content1C[1], content1C[3], content1C[2] }; ResetEvents(); ContentService.Sort(content1Csorted); IContent[] content1Cagain = Children(content1).ToArray(); Assert.AreEqual(4, content1Cagain.Length); Assert.AreEqual(content1C[0].Id, content1Cagain[0].Id); Assert.AreEqual(content1C[1].Id, content1Cagain[1].Id); Assert.AreEqual(content1C[2].Id, content1Cagain[3].Id); Assert.AreEqual(content1C[3].Id, content1Cagain[2].Id); Assert.AreEqual(3, _msgCount); Assert.AreEqual(4, _events.Count); int i = 0; int m = 0; #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); // content5 is not published Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); // content4 is published + changes m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content1C[3].Id}", _events[i++].ToString()); // content5 is not published Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content1C[2].Id}", _events[i].ToString()); // content4 is published #pragma warning restore SA1003 // Symbols should be spaced correctly } #endregion #region Trash // incl. trashing a published, unpublished content, w/changes // incl. trashing a branch, untrashing a single masked content // including emptying the recycle bin [Test] public void TrashUnpublishedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ResetEvents(); ContentService.MoveToRecycleBin(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } [Test] public void UntrashUnpublishedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.MoveToRecycleBin(content); ResetEvents(); ContentService.Move(content, -1); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } [Test] public void TrashPublishedContent() { // does 1) unpublish and 2) trash IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); ContentService.MoveToRecycleBin(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=m", _events[i++].ToString()); m++; ////Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/RemovePublished,Refresh/{1}", m, content.Id), _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } [Test] public void UntrashPublishedContent() { // same as unpublished as it's been unpublished IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ContentService.MoveToRecycleBin(content); ResetEvents(); ContentService.Move(content, -1); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; // trashing did /pm- (published, masked) // un-trashing cannot re-publish so /u?- (not-published, unchanged) // but because we *have* to change state to unpublished, it's /ux- and not /uu- Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p-u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } [Test] public void TrashPublishedContentWithChanges() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); content.Properties.First().SetValue("changed"); ContentService.Save(content); ResetEvents(); ContentService.MoveToRecycleBin(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content.Id}.p=m", _events[i++].ToString()); m++; ////Assert.AreEqual(string.Format("{0:000}: ContentCacheRefresher/RemovePublished,Refresh/{1}", m, content.Id), _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content.Id}", _events[i].ToString()); } [Test] public void TrashContentBranch() { IContent content1 = CreateBranch(); ResetEvents(); ContentService.MoveToRecycleBin(content1); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void EmptyRecycleBinContent() { ContentService.EmptyRecycleBin(Constants.Security.SuperUserId); IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.MoveToRecycleBin(content); ResetEvents(); ContentService.EmptyRecycleBin(Constants.Security.SuperUserId); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); var i = 0; var m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content.Id}", _events[i].ToString()); } [Test] public void EmptyRecycleBinContents() { ContentService.EmptyRecycleBin(Constants.Security.SuperUserId); IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.MoveToRecycleBin(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.MoveToRecycleBin(content2); ResetEvents(); ContentService.EmptyRecycleBin(Constants.Security.SuperUserId); Assert.AreEqual(3, _msgCount); Assert.AreEqual(4, _events.Count); var i = 0; var m = 0; Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1.Id}", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content2.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content1.Id}", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content2.Id}", _events[i].ToString()); } [Test] public void EmptyRecycleBinBranch() { ContentService.EmptyRecycleBin(Constants.Security.SuperUserId); IContent content1 = CreateBranch(); Assert.IsNotNull(content1); ContentService.MoveToRecycleBin(content1); ResetEvents(); IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); ContentService.EmptyRecycleBin(Constants.Security.SuperUserId); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); var i = 0; var m = 0; Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content5C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content5C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[3].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content4C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content4C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[2].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content3C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content3C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content2C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content2C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content1.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content1.Id}", _events[i].ToString()); } #endregion #region Delete [Test] public void DeleteUnpublishedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ResetEvents(); ContentService.Delete(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content.Id}", _events[i].ToString()); } [Test] public void DeletePublishedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); ContentService.Delete(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content.Id}", _events[i].ToString()); } [Test] public void DeletePublishedContentWithChanges() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); content.Properties.First().SetValue("changed"); ContentService.Save(content); ResetEvents(); ContentService.Delete(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content.Id}", _events[i].ToString()); } [Test] public void DeleteMaskedPublishedContent() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ContentService.Unpublish(content1); ResetEvents(); ContentService.Delete(content2); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content2.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content2.Id}", _events[i].ToString()); } [Test] public void DeleteBranch() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); // get them before they are deleted! IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); ResetEvents(); ContentService.Delete(content1); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content5C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content5C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[3].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content4C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content4C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[2].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content3C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content3C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content2C[1].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content2C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Remove/{content1C[0].Id}", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Remove/{content1.Id}", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/Remove/{content1.Id}", _events[i].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } #endregion #region Move [Test] public void MoveUnpublishedContentUnderUnpublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MovePublishedContentUnderUnpublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); } [Test] public void MovePublishedContentWithChangesUnderUnpublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ContentService.Save(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); } [Test] public void MoveUnpublishedContentUnderPublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MoveUnpublishedContentUnderMasked() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ContentService.Unpublish(content2); ResetEvents(); ContentService.Move(content1, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MovePublishedContentUnderPublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MovePublishedContentUnderMasked() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); IContent content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ContentService.Unpublish(content2); ResetEvents(); ContentService.Move(content1, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MovePublishedContentWithChangesUnderPublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ContentService.Save(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MovePublishedContentWithChangesUnderMasked() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ContentService.Save(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); IContent content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ContentService.Unpublish(content2); ResetEvents(); ContentService.Move(content1, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } [Test] public void MoveMaskedPublishedContentUnderPublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ContentService.Unpublish(content1); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ResetEvents(); ContentService.Move(content2, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content2.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content2.Id}", _events[i].ToString()); } [Test] public void MoveMaskedPublishedContentUnderMasked() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ContentService.Unpublish(content1); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); IContent content4 = CreateContent(content3.Id); Assert.IsNotNull(content4); ContentService.SaveAndPublish(content4); ContentService.Unpublish(content3); ResetEvents(); ContentService.Move(content2, content4.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content2.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content2.Id}", _events[i].ToString()); } [Test] public void MoveMaskedPublishedContentWithChangesUnderPublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ContentService.Save(content2); ContentService.Unpublish(content1); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ResetEvents(); ContentService.Move(content2, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content2.Id}.p=p", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content2.Id}", _events[i].ToString()); } [Test] public void MoveMaskedPublishedContentWithChangesUnderMasked() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ContentService.Save(content2); ContentService.Unpublish(content1); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); IContent content4 = CreateContent(content3.Id); Assert.IsNotNull(content4); ContentService.SaveAndPublish(content4); ContentService.Unpublish(content3); ResetEvents(); ContentService.Move(content2, content4.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content2.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content2.Id}", _events[i].ToString()); } [Test] public void MoveMaskedPublishedContentUnderUnpublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ContentService.Unpublish(content1); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ResetEvents(); ContentService.Move(content2, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content2.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content2.Id}", _events[i].ToString()); } [Test] public void MoveMaskedPublishedContentWithChangesUnderUnpublished() { IContent content1 = CreateContent(); Assert.IsNotNull(content1); ContentService.SaveAndPublish(content1); IContent content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ContentService.Save(content2); ContentService.Unpublish(content1); IContent content3 = CreateContent(); Assert.IsNotNull(content3); ResetEvents(); ContentService.Move(content2, content3.Id); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content2.Id}.p=m", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content2.Id}", _events[i].ToString()); } [Test] public void MoveContentBranchUnderUnpublished() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void MoveContentBranchUnderPublished() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ResetEvents(); ContentService.Move(content1, content2.Id); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void MoveContentBranchUnderMasked() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); IContent content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ContentService.Unpublish(content2); ResetEvents(); ContentService.Move(content1, content3.Id); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void MoveContentBranchBackFromPublished() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); ContentService.Move(content1, content2.Id); ResetEvents(); ContentService.Move(content1, -1); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void MoveContentBranchBackFromUnpublished() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.Move(content1, content2.Id); ResetEvents(); ContentService.Move(content1, -1); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } [Test] public void MoveContentBranchBackFromMasked() { IContent content1 = CreateBranch(); Assert.IsNotNull(content1); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.SaveAndPublish(content2); IContent content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); ContentService.SaveAndPublish(content3); ContentService.Unpublish(content2); ContentService.Move(content1, content3.Id); ResetEvents(); ContentService.Move(content1, -1); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; IContent[] content1C = Children(content1).ToArray(); IContent[] content2C = Children(content1C[0]).ToArray(); IContent[] content3C = Children(content1C[1]).ToArray(); IContent[] content4C = Children(content1C[2]).ToArray(); IContent[] content5C = Children(content1C[3]).ToArray(); #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } #endregion #region Copy [Test] public void CopyUnpublishedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ResetEvents(); IContent copy = ContentService.Copy(content, Constants.System.Root, false); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{copy.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{copy.Id}", _events[i].ToString()); } [Test] public void CopyPublishedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); IContent copy = ContentService.Copy(content, Constants.System.Root, false); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{copy.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{copy.Id}", _events[i].ToString()); } [Test] public void CopyMaskedContent() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); IContent content2 = CreateContent(); Assert.IsNotNull(content2); ContentService.Move(content, content2.Id); ResetEvents(); IContent copy = ContentService.Copy(content, Constants.System.Root, false); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{copy.Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{copy.Id}", _events[i].ToString()); } [Test] public void CopyBranch() { IContent content = CreateBranch(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); ResetEvents(); IContent copy = ContentService.Copy(content, Constants.System.Root, false); IContent[] copyC = Children(copy).ToArray(); IContent[] copy2C = Children(copyC[0]).ToArray(); IContent[] copy3C = Children(copyC[1]).ToArray(); IContent[] copy4C = Children(copyC[2]).ToArray(); IContent[] copy5C = Children(copyC[3]).ToArray(); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); int i = 0; int m = 0; #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy.Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy2C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy2C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[2].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[1].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy5C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{copy5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{copy.Id}", _events[i].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } #endregion #region Rollback [Test] public void Rollback() { IContent content = CreateContent(); Assert.IsNotNull(content); ContentService.SaveAndPublish(content); int v1 = content.VersionId; content.Properties.First().SetValue("changed"); ContentService.SaveAndPublish(content); int v2 = content.VersionId; content.Properties.First().SetValue("again"); ContentService.SaveAndPublish(content); int v3 = content.VersionId; Console.WriteLine(v1); Console.WriteLine(v2); Console.WriteLine(v3); ResetEvents(); content.CopyFrom(ContentService.GetVersion(v2)); ContentService.Save(content); Assert.AreEqual(2, _msgCount); Assert.AreEqual(2, _events.Count); int i = 0; int m = 0; #pragma warning disable SA1003 // Symbols should be spaced correctly (justification: suppression necessary here as it's used in an interpolated string format. Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshNode/{content.Id}", _events[i].ToString()); #pragma warning restore SA1003 // Symbols should be spaced correctly } #endregion #region Misc [Test] public void ContentRemembers() { IContent content = ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); ContentService.Save(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsFalse(content.WasPropertyDirty("Published")); ContentService.SaveAndPublish(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsTrue(content.WasPropertyDirty("Published")); // has just been published ContentService.SaveAndPublish(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsFalse(content.WasPropertyDirty("Published")); // was published already } [Test] public void HasInitialContent() => Assert.AreEqual(5, ContentService.Count()); #endregion #region TODO // all content type events #endregion public class LocalServerMessenger : ServerMessengerBase { public LocalServerMessenger() : base(false) { } public override void SendMessages() { } public override void Sync() { } protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) { } } } } #pragma warning restore SA1124 // Do not use regions