Files
Umbraco-CMS/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServicePublishBranchTests.cs
Emma L Garland ac575662ac Resolved more warnings, and marked more warning types as errors (#16991)
* Fix warnings SA1111, SA1028, SA1500, IDE1270  in Umbraco.Web.Website, and updated rules.

* Remove warnings: IDE0270: Null check can be simplified

* More SqlServer project warnings resolved

* CS0105 namespace appeared already

* Suppress warning until implementation:

#pragma warning disable CS0162 // Unreachable code detected
#pragma warning disable CS0618 // Type or member is obsolete

CS0162 remove unreachable code
SA1028 remove trailing whitespace
SA1106 no empty statements
CS1570 malformed XML
CS1572 corrected xml parameter
CS1573 param tag added
IDE0007 var not explicit
IDE0008 explicit not var
IDE0057 simplify substring
IDE0074 compound assignment
CA1825 array.empty

Down to 3479 warnings

* - SA1116, SA117 params on same line
- IDE0057 substring simplified

Specific warnings for Umbraco.Tests.Benchmarks

* Fixed IDE0074 compound assignment and added specific warnings for Umbraco.Tests.Common

* Specific warnings for Umbraco.Tests.Integration and Umbraco.Tests.Common

Fixed:

- SA1111, SA1116, SA117 params and line formatting (not all as there are many)
- SA1122 string.Empty
- IDE0057 simplify substring
- IDE0044,IDE0044 make field readonly
- IDE1006 naming rule violation (add _)
- SA1111 closing parenthesis on line of last parameter
- SA1649 filename match type name
- SA1312,SA1306 lowercase variable and field names

* Fixed various warnings where they are more straight-forward, including:

- SA1649 file name match type name
- SA111 parenthesis on line of last parameter
- IDE0028 simplify collection initializer
- SA1306 lower-case letter field
- IDE044 readonly field
- SA1122 string.Empty
- SA1116 params same line
- IDE1006 upper casing
- IDE0041 simplify null check

Updated the following projects to only list their remaining specific warning codes:

- Umbraco.Tests.UnitTests

Typo in `Umbraco.Web.Website` project

* Reverted test change

* Now 1556 warnings.

Fixed various warnings where they are more straight-forward, including:

- SA1111/SA1116/SA1119 parenthesis
- SA1117 params
- SA1312 lowercase variable
- SA1121 built-in type
- SA1500/SA1513/SA1503 formatting braces
- SA1400 declare access modifier
- SA1122 string.Empty
- SA1310 no underscore
- IDE0049 name simplified
- IDE0057 simplify substring
- IDE0074 compound assignment
- IDE0032 use auto-property
- IDE0037 simplify member name
- IDE0008 explicit type not var
- IDE0016/IDE0270/IDE0041 simplify null checks
- IDE0048/SA1407 clarity in arithmetic
- IDE1006 correct param names
- IDE0042 deconstruct variable
- IDE0044 readonly
- IDE0018 inline variable declarations
- IDE0074/IDE0054 compound assignment
- IDE1006 naming
- CS1573 param XML
- CS0168 unused variable

Comment formatting in project files for consistency.

Updated all projects to only list remaining specific warning codes as warnings instead of errors (errors is now default).

* Type not var, and more warning exceptions

* Tweaked merge issue, readded comment about rollback

* Readded comment re rollback.

* Readded comments

* Comment tweak

* Comment tweak
2024-09-24 12:56:28 +01:00

511 lines
21 KiB
C#

// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Tests.Common.Attributes;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
// ReSharper disable CommentTypo
// ReSharper disable StringLiteralTypo
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true,
WithApplication = true)]
public class ContentServicePublishBranchTests : UmbracoIntegrationTest
{
private IContentService ContentService => GetRequiredService<IContentService>();
private ILanguageService LanguageService => GetRequiredService<ILanguageService>();
private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
[TestCase(1)] // publish w/ culture: content.AvailableCultures.ToArray()
[TestCase(2)] // publish w/ cultures: new [] { "*" }
[TestCase(3)] // publish w/ cultures: Array.Empty<string>()
[LongRunning]
public void Can_Publish_Invariant_Branch(int method)
{
CreateTypes(out var iContentType, out _);
IContent iRoot = new Content("iroot", -1, iContentType);
iRoot.SetValue("ip", "iroot");
ContentService.Save(iRoot);
IContent ii1 = new Content("ii1", iRoot, iContentType);
ii1.SetValue("ip", "vii1");
ContentService.Save(ii1);
IContent ii2 = new Content("ii2", iRoot, iContentType);
ii2.SetValue("ip", "vii2");
ContentService.Save(ii2);
// iroot !published !edited
// ii1 !published !edited
// ii2 !published !edited
// !force = publishes those that are actually published, and have changes
// here: root (root is always published)
var r = PublishInvariantBranch(iRoot, false, method).ToArray();
// not forcing, ii1 and ii2 not published yet: only root got published
AssertPublishResults(r, x => x.Content.Name, "iroot");
AssertPublishResults(r, x => x.Result, PublishResultType.SuccessPublish);
// prepare
ContentService.Publish(iRoot, iRoot.AvailableCultures.ToArray());
ContentService.Publish(ii1, ii1.AvailableCultures.ToArray());
IContent ii11 = new Content("ii11", ii1, iContentType);
ii11.SetValue("ip", "vii11");
ContentService.Save(ii11);
ContentService.Publish(ii11, ii11.AvailableCultures.ToArray());
IContent ii12 = new Content("ii12", ii1, iContentType);
ii11.SetValue("ip", "vii12");
ContentService.Save(ii12);
ContentService.Publish(ii2, ii2.AvailableCultures.ToArray());
IContent ii21 = new Content("ii21", ii2, iContentType);
ii21.SetValue("ip", "vii21");
ContentService.Save(ii21);
ContentService.Publish(ii21, ii21.AvailableCultures.ToArray());
IContent ii22 = new Content("ii22", ii2, iContentType);
ii22.SetValue("ip", "vii22");
ContentService.Save(ii22);
ContentService.Unpublish(ii2);
// iroot published !edited
// ii1 published !edited
// ii11 published !edited
// ii12 !published !edited
// ii2 !published !edited
// ii21 (published) !edited
// ii22 !published !edited
// !force = publishes those that are actually published, and have changes
// here: nothing
r = PublishInvariantBranch(iRoot, false, method).ToArray();
// not forcing, ii12 and ii2, ii21, ii22 not published yet: only root, ii1, ii11 got published
AssertPublishResults(r, x => x.Content.Name, "iroot", "ii1", "ii11");
AssertPublishResults(
r,
x => x.Result,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready);
// prepare
iRoot.SetValue("ip", "changed");
ContentService.Save(iRoot);
ii11.SetValue("ip", "changed");
ContentService.Save(ii11);
// iroot published edited ***
// ii1 published !edited
// ii11 published edited ***
// ii12 !published !edited
// ii2 !published !edited
// ii21 (published) !edited
// ii22 !published !edited
// !force = publishes those that are actually published, and have changes
// here: iroot and ii11
// not forcing, ii12 and ii2, ii21, ii22 not published yet: only root, ii1, ii11 got published
r = PublishInvariantBranch(iRoot, false, method).ToArray();
AssertPublishResults(r, x => x.Content.Name, "iroot", "ii1", "ii11");
AssertPublishResults(
r,
x => x.Result,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish);
// force = publishes everything that has changes
// here: ii12, ii2, ii22 - ii21 was published already but masked
r = PublishInvariantBranch(iRoot, true, method).ToArray();
AssertPublishResults(
r,
x => x.Content.Name,
"iroot",
"ii1",
"ii11",
"ii12",
"ii2",
"ii21",
"ii22");
AssertPublishResults(
r,
x => x.Result,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublishAlready, // was masked
PublishResultType.SuccessPublish);
ii21 = ContentService.GetById(ii21.Id);
Assert.IsTrue(ii21.Published);
}
[Test]
public void Can_Publish_Variant_Branch_When_No_Changes_On_Root_All_Cultures()
{
CreateTypes(out _, out var vContentType);
// create/publish root
IContent vRoot = new Content("vroot", -1, vContentType, "de");
vRoot.SetCultureName("vroot.de", "de");
vRoot.SetCultureName("vroot.ru", "ru");
vRoot.SetCultureName("vroot.es", "es");
vRoot.SetValue("ip", "vroot");
vRoot.SetValue("vp", "vroot.de", "de");
vRoot.SetValue("vp", "vroot.ru", "ru");
vRoot.SetValue("vp", "vroot.es", "es");
ContentService.Save(vRoot);
ContentService.Publish(vRoot, vRoot.AvailableCultures.ToArray());
// create/publish child
IContent iv1 = new Content("iv1", vRoot, vContentType, "de");
iv1.SetCultureName("iv1.de", "de");
iv1.SetCultureName("iv1.ru", "ru");
iv1.SetCultureName("iv1.es", "es");
iv1.SetValue("ip", "iv1");
iv1.SetValue("vp", "iv1.de", "de");
iv1.SetValue("vp", "iv1.ru", "ru");
iv1.SetValue("vp", "iv1.es", "es");
ContentService.Save(iv1);
ContentService.Publish(iv1, iv1.AvailableCultures.ToArray());
// update the child
iv1.SetValue("vp", "UPDATED-iv1.de", "de");
ContentService.Save(iv1);
var r = ContentService.PublishBranch(vRoot, false, vRoot.AvailableCultures.ToArray())
.ToArray(); // no culture specified so "*" is used, so all cultures
Assert.AreEqual(PublishResultType.SuccessPublishAlready, r[0].Result);
Assert.AreEqual(PublishResultType.SuccessPublishCulture, r[1].Result);
}
[Test]
public void Can_Publish_Variant_Branch_When_No_Changes_On_Root_Specific_Culture()
{
CreateTypes(out _, out var vContentType);
// create/publish root
IContent vRoot = new Content("vroot", -1, vContentType, "de");
vRoot.SetCultureName("vroot.de", "de");
vRoot.SetCultureName("vroot.ru", "ru");
vRoot.SetCultureName("vroot.es", "es");
vRoot.SetValue("ip", "vroot");
vRoot.SetValue("vp", "vroot.de", "de");
vRoot.SetValue("vp", "vroot.ru", "ru");
vRoot.SetValue("vp", "vroot.es", "es");
ContentService.Save(vRoot);
ContentService.Publish(vRoot, vRoot.AvailableCultures.ToArray());
// create/publish child
IContent iv1 = new Content("iv1", vRoot, vContentType, "de");
iv1.SetCultureName("iv1.de", "de");
iv1.SetCultureName("iv1.ru", "ru");
iv1.SetCultureName("iv1.es", "es");
iv1.SetValue("ip", "iv1");
iv1.SetValue("vp", "iv1.de", "de");
iv1.SetValue("vp", "iv1.ru", "ru");
iv1.SetValue("vp", "iv1.es", "es");
ContentService.Save(iv1);
ContentService.Publish(iv1, iv1.AvailableCultures.ToArray());
// update the child
iv1.SetValue("vp", "UPDATED-iv1.de", "de");
var saveResult = ContentService.Save(iv1);
var r = ContentService.PublishBranch(vRoot, false, new [] { "de" }).ToArray();
Assert.AreEqual(PublishResultType.SuccessPublishAlready, r[0].Result);
Assert.AreEqual(PublishResultType.SuccessPublishCulture, r[1].Result);
}
[Test]
public void Can_Publish_Variant_Branch()
{
CreateTypes(out _, out var vContentType);
IContent vRoot = new Content("vroot", -1, vContentType, "de");
vRoot.SetCultureName("vroot.de", "de");
vRoot.SetCultureName("vroot.ru", "ru");
vRoot.SetCultureName("vroot.es", "es");
vRoot.SetValue("ip", "vroot");
vRoot.SetValue("vp", "vroot.de", "de");
vRoot.SetValue("vp", "vroot.ru", "ru");
vRoot.SetValue("vp", "vroot.es", "es");
ContentService.Save(vRoot);
IContent iv1 = new Content("iv1", vRoot, vContentType, "de");
iv1.SetCultureName("iv1.de", "de");
iv1.SetCultureName("iv1.ru", "ru");
iv1.SetCultureName("iv1.es", "es");
iv1.SetValue("ip", "iv1");
iv1.SetValue("vp", "iv1.de", "de");
iv1.SetValue("vp", "iv1.ru", "ru");
iv1.SetValue("vp", "iv1.es", "es");
ContentService.Save(iv1);
IContent iv2 = new Content("iv2", vRoot, vContentType, "de");
iv2.SetCultureName("iv2.de", "de");
iv2.SetCultureName("iv2.ru", "ru");
iv2.SetCultureName("iv2.es", "es");
iv2.SetValue("ip", "iv2");
iv2.SetValue("vp", "iv2.de", "de");
iv2.SetValue("vp", "iv2.ru", "ru");
iv2.SetValue("vp", "iv2.es", "es");
ContentService.Save(iv2);
// vroot !published !edited
// iv1 !published !edited
// iv2 !published !edited
// !force = publishes those that are actually published, and have changes
// here: nothing
var r = ContentService.PublishBranch(vRoot, false, new[] { "*" }).ToArray(); // no culture specified = all cultures
// not forcing, iv1 and iv2 not published yet: only root got published
AssertPublishResults(r, x => x.Content.Name, "vroot.de");
AssertPublishResults(r, x => x.Result, PublishResultType.SuccessPublishCulture);
// prepare
vRoot.SetValue("ip", "changed");
vRoot.SetValue("vp", "changed.de", "de");
vRoot.SetValue("vp", "changed.ru", "ru");
vRoot.SetValue("vp", "changed.es", "es");
ContentService.Save(vRoot); // now root has drafts in all cultures
ContentService.Publish(iv1, new[] { "de", "ru" }); // now iv1 de and ru are published
iv1.SetValue("ip", "changed");
iv1.SetValue("vp", "changed.de", "de");
iv1.SetValue("vp", "changed.ru", "ru");
iv1.SetValue("vp", "changed.es", "es");
ContentService.Save(iv1); // now iv1 has drafts in all cultures
// validate - everything published for root, because no culture was specified = all
Assert.IsTrue(vRoot.Published);
Assert.IsTrue(vRoot.IsCulturePublished("de"));
Assert.IsTrue(vRoot.IsCulturePublished("ru"));
Assert.IsTrue(vRoot.IsCulturePublished("es"));
// validate - only some cultures published for iv1
Assert.IsTrue(iv1.Published);
Assert.IsTrue(iv1.IsCulturePublished("de"));
Assert.IsTrue(iv1.IsCulturePublished("ru"));
Assert.IsFalse(iv1.IsCulturePublished("es"));
r = ContentService.PublishBranch(vRoot, false, new[] { "de" }).ToArray();
// not forcing, iv2 not published yet: only root and iv1 got published
AssertPublishResults(r, x => x.Content.Name, "vroot.de", "iv1.de");
AssertPublishResults(
r,
x => x.Result,
PublishResultType.SuccessPublishCulture,
PublishResultType.SuccessPublishCulture);
// reload - SaveAndPublishBranch has modified other instances
Reload(ref iv1);
Reload(ref iv2);
// validate - root
Assert.IsTrue(vRoot.Published);
Assert.IsTrue(vRoot.IsCulturePublished("de"));
Assert.IsFalse(vRoot.IsCultureEdited("de")); // no drafts, this was just published
Assert.IsTrue(vRoot.IsCulturePublished("ru"));
Assert.IsTrue(vRoot.IsCultureEdited("ru")); // has draft
Assert.IsTrue(vRoot.IsCulturePublished("es"));
Assert.IsTrue(vRoot.IsCultureEdited("es")); // has draft
Assert.AreEqual("changed", vRoot.GetValue("ip", published: true)); // publishing de implies publishing invariants
Assert.AreEqual("changed.de", vRoot.GetValue("vp", "de", published: true));
// validate - de and ru are published, es has not been published
Assert.IsTrue(iv1.Published);
Assert.IsTrue(iv1.IsCulturePublished("de"));
Assert.IsTrue(iv1.IsCulturePublished("ru"));
Assert.IsFalse(iv1.IsCulturePublished("es"));
Assert.AreEqual("changed", iv1.GetValue("ip", published: true));
Assert.AreEqual("changed.de", iv1.GetValue("vp", "de", published: true));
Assert.AreEqual("iv1.ru", iv1.GetValue("vp", "ru", published: true));
}
private void Can_Publish_Mixed_Branch(out IContent iRoot, out IContent ii1, out IContent iv11)
{
// invariant root -> variant -> invariant
// variant root -> variant -> invariant
// variant root -> invariant -> variant
CreateTypes(out var iContentType, out var vContentType);
// invariant root -> invariant -> variant
iRoot = new Content("iroot", -1, iContentType);
iRoot.SetValue("ip", "iroot");
ContentService.Save(iRoot);
ContentService.Publish(iRoot, iRoot.AvailableCultures.ToArray());
ii1 = new Content("ii1", iRoot, iContentType);
ii1.SetValue("ip", "vii1");
ContentService.Save(ii1);
ContentService.Publish(ii1, ii1.AvailableCultures.ToArray());
ii1.SetValue("ip", "changed");
ContentService.Save(ii1);
iv11 = new Content("iv11.de", ii1, vContentType, "de");
iv11.SetValue("ip", "iv11");
iv11.SetValue("vp", "iv11.de", "de");
iv11.SetValue("vp", "iv11.ru", "ru");
iv11.SetValue("vp", "iv11.es", "es");
ContentService.Save(iv11);
iv11.SetCultureName("iv11.ru", "ru");
ContentService.Save(iv11);
var xxx = ContentService.Publish(iv11, new[] { "de", "ru" });
Assert.AreEqual("iv11.de", iv11.GetValue("vp", "de", published: true));
Assert.AreEqual("iv11.ru", iv11.GetValue("vp", "ru", published: true));
iv11.SetValue("ip", "changed");
iv11.SetValue("vp", "changed.de", "de");
iv11.SetValue("vp", "changed.ru", "ru");
ContentService.Save(iv11);
}
[Test]
public void Can_Publish_Mixed_Branch_1()
{
Can_Publish_Mixed_Branch(out var iRoot, out var ii1, out var iv11);
var r = ContentService.PublishBranch(iRoot, false, new[] { "de" }).ToArray();
AssertPublishResults(r, x => x.Content.Name, "iroot", "ii1", "iv11.de");
AssertPublishResults(
r,
x => x.Result,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublishCulture);
// reload - SaveAndPublishBranch has modified other instances
Reload(ref ii1);
Reload(ref iv11);
// the invariant child has been published
// the variant child has been published for 'de' only
Assert.AreEqual("changed", ii1.GetValue("ip", published: true));
Assert.AreEqual("changed", iv11.GetValue("ip", published: true));
Assert.AreEqual("changed.de", iv11.GetValue("vp", "de", published: true));
Assert.AreEqual("iv11.ru", iv11.GetValue("vp", "ru", published: true));
}
[Test]
public void Can_Publish_MixedBranch_2()
{
Can_Publish_Mixed_Branch(out var iRoot, out var ii1, out var iv11);
var r = ContentService.PublishBranch(iRoot, false, new[] { "de", "ru" }).ToArray();
AssertPublishResults(r, x => x.Content.Name, "iroot", "ii1", "iv11.de");
AssertPublishResults(
r,
x => x.Result,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublishCulture);
// reload - SaveAndPublishBranch has modified other instances
Reload(ref ii1);
Reload(ref iv11);
// the invariant child has been published
// the variant child has been published for 'de' and 'ru'
Assert.AreEqual("changed", ii1.GetValue("ip", published: true));
Assert.AreEqual("changed", iv11.GetValue("ip", published: true));
Assert.AreEqual("changed.de", iv11.GetValue("vp", "de", published: true));
Assert.AreEqual("changed.ru", iv11.GetValue("vp", "ru", published: true));
}
private void AssertPublishResults<T>(PublishResult[] values, Func<PublishResult, T> getter, params T[] expected)
{
if (expected.Length != values.Length)
{
Console.WriteLine(string.Join(", ", values.Select(x => getter(x).ToString())));
}
Assert.AreEqual(expected.Length, values.Length);
for (var i = 0; i < values.Length; i++)
{
var value = getter(values[i]);
Assert.AreEqual(expected[i], value, $"Expected {expected[i]} at {i} but got {value}.");
}
}
private void Reload(ref IContent document)
=> document = ContentService.GetById(document.Id);
private void CreateTypes(out IContentType iContentType, out IContentType vContentType)
{
var langDe = new Language("de", "German") { IsDefault = true };
LanguageService.CreateAsync(langDe, Constants.Security.SuperUserKey).GetAwaiter().GetResult();
var langRu = new Language("ru", "Russian");
LanguageService.CreateAsync(langRu, Constants.Security.SuperUserKey).GetAwaiter().GetResult();
var langEs = new Language("es", "Spanish");
LanguageService.CreateAsync(langEs, Constants.Security.SuperUserKey).GetAwaiter().GetResult();
iContentType = new ContentType(ShortStringHelper, -1)
{
Alias = "ict",
Name = "Invariant Content Type",
Variations = ContentVariation.Nothing
};
iContentType.AddPropertyType(
new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar,
"ip")
{ Variations = ContentVariation.Nothing });
ContentTypeService.Save(iContentType);
vContentType = new ContentType(ShortStringHelper, -1)
{
Alias = "vct",
Name = "Variant Content Type",
Variations = ContentVariation.Culture
};
vContentType.AddPropertyType(
new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar,
"ip")
{ Variations = ContentVariation.Nothing });
vContentType.AddPropertyType(
new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar,
"vp")
{ Variations = ContentVariation.Culture });
ContentTypeService.Save(vContentType);
}
private IEnumerable<PublishResult> PublishInvariantBranch(IContent content, bool force, int method)
{
// ReSharper disable RedundantArgumentDefaultValue
// ReSharper disable ArgumentsStyleOther
switch (method)
{
case 1:
return ContentService.PublishBranch(content, force, content.AvailableCultures.ToArray());
case 2:
return ContentService.PublishBranch(content, force, cultures: new[] { "*" });
case 3:
return ContentService.PublishBranch(content, force, cultures: Array.Empty<string>());
default:
throw new ArgumentOutOfRangeException(nameof(method));
}
// ReSharper restore RedundantArgumentDefaultValue
// ReSharper restore ArgumentsStyleOther
}
}