diff --git a/src/Umbraco.Configuration/Models/ConnectionStrings.cs b/src/Umbraco.Configuration/Models/ConnectionStrings.cs index 22a0bde571..0e6ca846aa 100644 --- a/src/Umbraco.Configuration/Models/ConnectionStrings.cs +++ b/src/Umbraco.Configuration/Models/ConnectionStrings.cs @@ -45,9 +45,14 @@ namespace Umbraco.Configuration.Models } } - if (builder.TryGetValue("Server", out var s) && s is string server && builder.TryGetValue("Database", out var db) && db is string database) + if (builder.TryGetValue("Server", out var s) && s is string server && !string.IsNullOrEmpty(server)) { - if (!string.IsNullOrEmpty(server) && !string.IsNullOrEmpty(database)) + if (builder.TryGetValue("Database", out var db) && db is string database && !string.IsNullOrEmpty(database)) + { + return Constants.DbProviderNames.SqlServer; + } + + if (builder.TryGetValue("AttachDbFileName", out var a) && a is string attachDbFileName && !string.IsNullOrEmpty(attachDbFileName)) { return Constants.DbProviderNames.SqlServer; } diff --git a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs index 5931d73365..cc78577f7e 100644 --- a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs @@ -2,6 +2,7 @@ using LightInject.Microsoft.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using System; +using System.Diagnostics; using Umbraco.Composing; using Umbraco.Core.Composing.LightInject; using Umbraco.Core.Configuration; @@ -92,7 +93,7 @@ namespace Umbraco.Core.Composing _container.GetInstance(), _container.GetInstance()); } - + return provider; } diff --git a/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs index 0229961277..c9aaa4e908 100644 --- a/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Globalization; using Umbraco.Core.Models; using Umbraco.Tests.Common.Builders.Interfaces; @@ -17,7 +19,8 @@ namespace Umbraco.Tests.Common.Builders IWithTrashedBuilder, IWithLevelBuilder, IWithPathBuilder, - IWithSortOrderBuilder + IWithSortOrderBuilder, + IWithCultureInfoBuilder { private ContentTypeBuilder _contentTypeBuilder; private GenericDictionaryBuilder _propertyDataBuilder; @@ -33,7 +36,9 @@ namespace Umbraco.Tests.Common.Builders private string _path; private int? _sortOrder; private bool? _trashed; + private CultureInfo _cultureInfo; private IContentType _contentType; + private IDictionary _cultureNames = new Dictionary(); public ContentTypeBuilder AddContentType() { @@ -56,6 +61,7 @@ namespace Umbraco.Tests.Common.Builders var path = _path ?? $"-1,{id}"; var sortOrder = _sortOrder ?? 0; var trashed = _trashed ?? false; + var culture = _cultureInfo?.Name ?? null; if (_contentTypeBuilder is null && _contentType is null) { @@ -64,7 +70,7 @@ namespace Umbraco.Tests.Common.Builders var contentType = _contentType ?? _contentTypeBuilder.Build(); - var content = new Content(name, parentId, contentType) + var content = new Content(name, parentId, contentType, culture) { Id = id, Key = key, @@ -77,6 +83,12 @@ namespace Umbraco.Tests.Common.Builders Trashed = trashed, }; + foreach (var cultureName in _cultureNames) + { + content.SetCultureName(cultureName.Value, cultureName.Key); + } + + if (_propertyDataBuilder != null) { var propertyData = _propertyDataBuilder.Build(); @@ -99,6 +111,30 @@ namespace Umbraco.Tests.Common.Builders return this; } + public ContentBuilder WithCultureName(string culture, string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + if (_cultureNames.TryGetValue(culture, out _)) + { + _cultureNames.Remove(culture); + } + } + else + { + _cultureNames[culture] = name; + } + + return this; + } + + public GenericDictionaryBuilder AddPropertyData() + { + var builder = new GenericDictionaryBuilder(this); + _propertyDataBuilder = builder; + return builder; + } + int? IWithIdBuilder.Id { get => _id; @@ -163,7 +199,10 @@ namespace Umbraco.Tests.Common.Builders get => _parentId; set => _parentId = value; } - - + CultureInfo IWithCultureInfoBuilder.CultureInfo + { + get => _cultureInfo; + set => _cultureInfo = value; + } } } diff --git a/src/Umbraco.Tests.Common/Builders/ContentItemSaveBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentItemSaveBuilder.cs new file mode 100644 index 0000000000..13196a038b --- /dev/null +++ b/src/Umbraco.Tests.Common/Builders/ContentItemSaveBuilder.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Tests.Common.Builders.Interfaces; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Tests.Common.Builders +{ + public class ContentItemSaveBuilder : BuilderBase, + IWithIdBuilder, + IWithParentIdBuilder + { + private List> _variantBuilders = new List>(); + + private int? _id; + private int? _parentId; + private string _contentTypeAlias; + private ContentSaveAction? _action; + + public ContentVariantSaveBuilder AddVariant() + { + var builder = new ContentVariantSaveBuilder(this); + _variantBuilders.Add(builder); + return builder; + } + + public override ContentItemSave Build() + { + var id = _id ?? 0; + var parentId = _parentId ?? -1; + var contentTypeAlias = _contentTypeAlias ?? null; + var action = _action ?? ContentSaveAction.Save; + var variants = _variantBuilders.Select(x => x.Build()); + + return new TestContentItemSave() + { + Id = id, + ParentId = parentId, + ContentTypeAlias = contentTypeAlias, + Action = action, + Variants = variants, + }; + } + + int? IWithIdBuilder.Id + { + get => _id; + set => _id = value; + } + int? IWithParentIdBuilder.ParentId + { + get => _parentId; + set => _parentId = value; + } + + public ContentItemSaveBuilder WithAction(ContentSaveAction action) + { + _action = action; + return this; + } + + public ContentItemSaveBuilder WithContentTypeAlias(string contentTypeAlias) + { + _contentTypeAlias = contentTypeAlias; + return this; + } + } + + public class TestContentItemSave : ContentItemSave + { + } +} diff --git a/src/Umbraco.Tests.Common/Builders/ContentPropertyBasicBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentPropertyBasicBuilder.cs new file mode 100644 index 0000000000..35f76bfd6a --- /dev/null +++ b/src/Umbraco.Tests.Common/Builders/ContentPropertyBasicBuilder.cs @@ -0,0 +1,49 @@ +using Umbraco.Tests.Common.Builders.Interfaces; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Tests.Common.Builders +{ + public class ContentPropertyBasicBuilder : ChildBuilderBase, + IWithIdBuilder, IWithAliasBuilder + { + private int? _id; + private string _alias; + private object _value; + + public ContentPropertyBasicBuilder(TParent parentBuilder) : base(parentBuilder) + { + } + + public override ContentPropertyBasic Build() + { + var alias = _alias ?? null; + var id = _id ?? 0; + var value = _value ?? null; + + return new ContentPropertyBasic() + { + Alias = alias, + Id = id, + Value = value + }; + } + + public ContentPropertyBasicBuilder WithValue(object value) + { + _value = value; + + return this; + } + int? IWithIdBuilder.Id + { + get => _id; + set => _id = value; + } + + string IWithAliasBuilder.Alias + { + get => _alias; + set => _alias = value; + } + } +} diff --git a/src/Umbraco.Tests.Common/Builders/ContentTypeBaseBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentTypeBaseBuilder.cs index 28bf2705fb..d9838cbd50 100644 --- a/src/Umbraco.Tests.Common/Builders/ContentTypeBaseBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/ContentTypeBaseBuilder.cs @@ -43,6 +43,7 @@ namespace Umbraco.Tests.Common.Builders private string _thumbnail; private bool? _trashed; private bool? _isContainer; + protected ContentVariation? ContentVariation { get; set; } protected IShortStringHelper ShortStringHelper => new DefaultShortStringHelper(new DefaultShortStringHelperConfig()); @@ -81,6 +82,8 @@ namespace Umbraco.Tests.Common.Builders protected bool GetTrashed() => _trashed ?? false; protected bool GetIsContainer() => _isContainer ?? false; + protected ContentVariation GetContentVariation() => ContentVariation ?? Core.Models.ContentVariation.Nothing; + protected void BuildPropertyGroups(ContentTypeCompositionBase contentType, IEnumerable propertyGroups) { @@ -90,6 +93,8 @@ namespace Umbraco.Tests.Common.Builders } } + + protected void BuildPropertyTypeIds(ContentTypeCompositionBase contentType, int? propertyTypeIdsIncrementingFrom) { if (propertyTypeIdsIncrementingFrom.HasValue) diff --git a/src/Umbraco.Tests.Common/Builders/ContentTypeBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentTypeBuilder.cs index 3c7dc4c76d..7d51d26437 100644 --- a/src/Umbraco.Tests.Common/Builders/ContentTypeBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/ContentTypeBuilder.cs @@ -78,6 +78,7 @@ namespace Umbraco.Tests.Common.Builders CreatorId = GetCreatorId(), Trashed = GetTrashed(), IsContainer = GetIsContainer(), + Variations = GetContentVariation(), }; contentType.NoGroupPropertyTypes = _noGroupPropertyTypeBuilders.Select(x => x.Build()); @@ -99,10 +100,17 @@ namespace Umbraco.Tests.Common.Builders return contentType; } + public ContentTypeBuilder WithContentVariation(ContentVariation contentVariation) + { + ContentVariation = contentVariation; + return this; + } + int? IWithPropertyTypeIdsIncrementingFrom.PropertyTypeIdsIncrementingFrom { get => _propertyTypeIdsIncrementingFrom; set => _propertyTypeIdsIncrementingFrom = value; } + } } diff --git a/src/Umbraco.Tests.Common/Builders/ContentVariantSaveBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentVariantSaveBuilder.cs new file mode 100644 index 0000000000..da031a804c --- /dev/null +++ b/src/Umbraco.Tests.Common/Builders/ContentVariantSaveBuilder.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Umbraco.Tests.Common.Builders.Interfaces; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Tests.Common.Builders +{ + public class ContentVariantSaveBuilder : ChildBuilderBase, + IWithNameBuilder, + IWithCultureInfoBuilder + { + private List>> _propertyBuilders = new List>>(); + + + private string _name; + private CultureInfo _cultureInfo; + private bool? _save; + private bool? _publish; + + public ContentVariantSaveBuilder(TParent parentBuilder) : base(parentBuilder) + { + } + + public ContentPropertyBasicBuilder> AddProperty() + { + var builder = new ContentPropertyBasicBuilder>(this); + _propertyBuilders.Add(builder); + return builder; + } + + public override ContentVariantSave Build() + { + var name = _name ?? null; + var culture = _cultureInfo?.Name ?? null; + var save = _save ?? true; + var publish = _publish ?? true; + var properties = _propertyBuilders.Select(x => x.Build()); + + return new ContentVariantSave() + { + Name = name, + Culture = culture, + Save = save, + Publish = publish, + Properties = properties + }; + } + + string IWithNameBuilder.Name + { + get => _name; + set => _name = value; + } + + CultureInfo IWithCultureInfoBuilder.CultureInfo + { + get => _cultureInfo; + set => _cultureInfo = value; + } + } +} diff --git a/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs b/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs index 92acaeca46..b5294d85fd 100644 --- a/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs +++ b/src/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs @@ -197,7 +197,7 @@ namespace Umbraco.Tests.Common.Builders.Extensions public static T WithCultureInfo(this T builder, string name) where T : IWithCultureInfoBuilder { - builder.CultureInfo = new CultureInfo(name); + builder.CultureInfo = CultureInfo.GetCultureInfo(name); return builder; } } diff --git a/src/Umbraco.Tests.Common/Builders/Extensions/ContentItemSaveBuilderExtensions.cs b/src/Umbraco.Tests.Common/Builders/Extensions/ContentItemSaveBuilderExtensions.cs new file mode 100644 index 0000000000..c402049745 --- /dev/null +++ b/src/Umbraco.Tests.Common/Builders/Extensions/ContentItemSaveBuilderExtensions.cs @@ -0,0 +1,55 @@ +using System.Linq; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Common.Builders.Extensions +{ + public static class ContentItemSaveBuilderExtensions + { + + public static ContentItemSaveBuilder WithContent(this ContentItemSaveBuilder builder, IContent content) + { + builder.WithId(content.Id); + builder.WithContentTypeAlias(content.ContentType.Alias); + + if (content.CultureInfos.Count == 0) + { + var variantBuilder = builder.AddVariant(); + variantBuilder.WithName(content.Name); + + foreach (var contentProperty in content.Properties) + { + AddInvariantProperty(variantBuilder, contentProperty); + } + } + else + { + foreach (var contentCultureInfos in content.CultureInfos) + { + var variantBuilder = builder.AddVariant(); + + variantBuilder.WithName(contentCultureInfos.Name); + variantBuilder.WithCultureInfo(contentCultureInfos.Culture); + + foreach (var contentProperty in content.Properties) + { + AddInvariantProperty(variantBuilder, contentProperty); + } + } + } + + + + return builder; + } + + private static void AddInvariantProperty(ContentVariantSaveBuilder variantBuilder, IProperty contentProperty) + { + variantBuilder + .AddProperty() + .WithId(contentProperty.Id) + .WithAlias(contentProperty.Alias) + .WithValue(contentProperty.GetValue()) + .Done(); + } + } +} diff --git a/src/Umbraco.Tests.Common/Builders/LanguageBuilder.cs b/src/Umbraco.Tests.Common/Builders/LanguageBuilder.cs index 953a64c5ed..88c8fa4639 100644 --- a/src/Umbraco.Tests.Common/Builders/LanguageBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/LanguageBuilder.cs @@ -68,9 +68,9 @@ namespace Umbraco.Tests.Common.Builders return new Language(Mock.Of(), cultureInfo.Name) { - Id = _id ?? 1, - CultureName = cultureInfo.TwoLetterISOLanguageName, - IsoCode = new RegionInfo(cultureInfo.LCID).Name, + Id = _id ?? 0, + CultureName = cultureInfo.EnglishName, + IsoCode = cultureInfo.Name, Key = key, CreateDate = createDate, UpdateDate = updateDate, diff --git a/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs index 52780e6d4f..3961657afe 100644 --- a/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs @@ -23,11 +23,13 @@ namespace Umbraco.Tests.Integration.Extensions /// /// /// + /// /// public static IApplicationBuilder UseTestLocalDb(this IApplicationBuilder app, string workingDirectory, - UmbracoIntegrationTest integrationTest) + UmbracoIntegrationTest integrationTest, out string connectionString) { + connectionString = null; var dbFilePath = Path.Combine(workingDirectory, "LocalDb"); // get the currently set db options @@ -47,6 +49,7 @@ namespace Umbraco.Tests.Integration.Extensions app.ApplicationServices.GetRequiredService(), app.ApplicationServices.GetRequiredService()); + switch (testOptions.Database) { case UmbracoTestOptions.Database.NewSchemaPerTest: @@ -113,10 +116,9 @@ namespace Umbraco.Tests.Integration.Extensions default: throw new ArgumentOutOfRangeException(nameof(testOptions), testOptions, null); } - + connectionString = db.ConnectionString; return app; } - } diff --git a/src/Umbraco.Tests.Integration/TestServer/Controllers/BackOfficeAssetsControllerTests.cs b/src/Umbraco.Tests.Integration/TestServer/Controllers/BackOfficeAssetsControllerTests.cs deleted file mode 100644 index 53feaa7cc0..0000000000 --- a/src/Umbraco.Tests.Integration/TestServer/Controllers/BackOfficeAssetsControllerTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using Umbraco.Extensions; -using Umbraco.Web.BackOffice.Controllers; - -namespace Umbraco.Tests.Integration.TestServer.Controllers -{ - public class BackOfficeAssetsControllerTests: UmbracoWebApplicationFactory - { - private LinkGenerator _linkGenerator; - - [OneTimeSetUp] - public void GivenARequestToTheController() - { - _linkGenerator = Services.GetRequiredService(); - } - - [Test] - public async Task EnsureSuccessStatusCode() - { - // Arrange - var client = CreateClient(); - var url = _linkGenerator.GetUmbracoApiService(x=>x.GetSupportedLocales()); - - // Act - var response = await client.GetAsync(url); - - // Assert - Assert.GreaterOrEqual((int)response.StatusCode, 200); - Assert.Less((int)response.StatusCode, 300); - } - } -} diff --git a/src/Umbraco.Tests.Integration/TestServer/UmbracoWebApplicationFactory.cs b/src/Umbraco.Tests.Integration/TestServer/UmbracoWebApplicationFactory.cs deleted file mode 100644 index 9be8c7099b..0000000000 --- a/src/Umbraco.Tests.Integration/TestServer/UmbracoWebApplicationFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Testing; -using Umbraco.Web.UI.BackOffice; - -namespace Umbraco.Tests.Integration.TestServer -{ - public class UmbracoWebApplicationFactory : WebApplicationFactory - { - } -} diff --git a/src/Umbraco.Tests.Integration/TestServerTest/Controllers/BackOfficeAssetsControllerTests.cs b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/BackOfficeAssetsControllerTests.cs new file mode 100644 index 0000000000..ad7fcb7b53 --- /dev/null +++ b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/BackOfficeAssetsControllerTests.cs @@ -0,0 +1,26 @@ +using System.Net; +using System.Threading.Tasks; +using NUnit.Framework; +using Umbraco.Tests.Testing; +using Umbraco.Web.BackOffice.Controllers; + +namespace Umbraco.Tests.Integration.TestServerTest.Controllers +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class BackOfficeAssetsControllerTests: UmbracoTestServerTestBase + { + [Test] + public async Task EnsureSuccessStatusCode() + { + // Arrange + var url = PrepareUrl(x=>x.GetSupportedLocales()); + + // Act + var response = await Client.GetAsync(url); + + // Assert + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + } +} diff --git a/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs new file mode 100644 index 0000000000..c18cb98509 --- /dev/null +++ b/src/Umbraco.Tests.Integration/TestServerTest/Controllers/ContentControllerTests.cs @@ -0,0 +1,377 @@ +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Tests.Common.Builders; +using Umbraco.Tests.Common.Builders.Extensions; +using Umbraco.Tests.Testing; +using Umbraco.Web.Common.Formatters; +using Umbraco.Web.Editors; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Tests.Integration.TestServerTest.Controllers +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class ContentControllerTests : UmbracoTestServerTestBase + { + [SetUp] + public void Setup() + { + var localizationService = GetRequiredService(); + + //Add another language + localizationService.Save(new LanguageBuilder() + .WithCultureInfo("da-DK") + .WithIsDefault(false) + .Build()); + } + + /// + /// Returns 404 if the content wasn't found based on the ID specified + /// + /// + [Test] + public async Task PostSave_Validate_Existing_Content() + { + var url = PrepareUrl(x => x.PostSave(null)); + + var contentService = GetRequiredService(); + var contentTypeService = GetRequiredService(); + + var contentType = new ContentTypeBuilder() + .WithId(0) + .AddPropertyType() + .WithAlias("title") + .WithValueStorageType(ValueStorageType.Integer) + .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox) + .WithName("Title") + .Done() + .WithContentVariation(ContentVariation.Nothing) + .Build(); + + contentTypeService.Save(contentType); + + var content = new ContentBuilder() + .WithId(0) + .WithName("Invariant") + .WithContentType(contentType) + .AddPropertyData() + .WithKeyValue("title", "Cool invariant title") + .Done() + .Build(); + contentService.SaveAndPublish(content); + + var model = new ContentItemSaveBuilder() + .WithContent(content) + .WithId(-1337) // HERE We overwrite the Id, so we don't expect to find it on the server + .Build(); + + // Act + var response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + // Assert + + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); +// Assert.AreEqual(")]}',\n{\"Message\":\"content was not found\"}", response.Item1.Content.ReadAsStringAsync().Result); +// +// //var obj = JsonConvert.DeserializeObject>(response.Item2); +// //Assert.AreEqual(0, obj.TotalItems); + } + + [Test] + public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving() + { + var url = PrepareUrl(x => x.PostSave(null)); + + var contentService = GetRequiredService(); + var contentTypeService = GetRequiredService(); + + var contentType = new ContentTypeBuilder() + .WithId(0) + .AddPropertyType() + .WithAlias("title") + .WithValueStorageType(ValueStorageType.Integer) + .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox) + .WithName("Title") + .Done() + .WithContentVariation(ContentVariation.Nothing) + .Build(); + + contentTypeService.Save(contentType); + + var content = new ContentBuilder() + .WithId(0) + .WithName("Invariant") + .WithContentType(contentType) + .AddPropertyData() + .WithKeyValue("title", "Cool invariant title") + .Done() + .Build(); + contentService.SaveAndPublish(content); + + var model = new ContentItemSaveBuilder() + .WithContent(content) + .Build(); + + // HERE we force the test to fail + model.Variants = model.Variants.Select(x=> + { + x.Save = false; + return x; + }); + + // Act + var response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + // Assert + + var body = await response.Content.ReadAsStringAsync(); + + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.AreEqual(")]}',\n{\"message\":\"No variants flagged for saving\"}", body); + } + + /// + /// Returns 404 if any of the posted properties dont actually exist + /// + /// + [Test] + public async Task PostSave_Validate_Properties_Exist() + { + var url = PrepareUrl(x => x.PostSave(null)); + + var contentService = GetRequiredService(); + var contentTypeService = GetRequiredService(); + + var contentType = new ContentTypeBuilder() + .WithId(0) + .AddPropertyType() + .WithAlias("title") + .WithValueStorageType(ValueStorageType.Integer) + .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox) + .WithName("Title") + .Done() + .WithContentVariation(ContentVariation.Nothing) + .Build(); + + contentTypeService.Save(contentType); + + var content = new ContentBuilder() + .WithId(0) + .WithName("Invariant") + .WithContentType(contentType) + .AddPropertyData() + .WithKeyValue("title", "Cool invariant title") + .Done() + .Build(); + contentService.SaveAndPublish(content); + + var model = new ContentItemSaveBuilder() + .WithId(content.Id) + .WithContentTypeAlias(content.ContentType.Alias) + .AddVariant() + .AddProperty() + .WithId(2) + .WithAlias("doesntexists") + .WithValue("Whatever") + .Done() + .Done() + .Build(); + + + // Act + var response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + // Assert + + var body = await response.Content.ReadAsStringAsync(); + + body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix); + + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + } + + [Test] + public async Task PostSave_Simple_Invariant() + { + var url = PrepareUrl(x => x.PostSave(null)); + + var contentService = GetRequiredService(); + var contentTypeService = GetRequiredService(); + + var contentType = new ContentTypeBuilder() + .WithId(0) + .AddPropertyType() + .WithAlias("title") + .WithValueStorageType(ValueStorageType.Integer) + .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox) + .WithName("Title") + .Done() + .WithContentVariation(ContentVariation.Nothing) + .Build(); + + contentTypeService.Save(contentType); + + var content = new ContentBuilder() + .WithId(0) + .WithName("Invariant") + .WithContentType(contentType) + .AddPropertyData() + .WithKeyValue("title", "Cool invariant title") + .Done() + .Build(); + contentService.SaveAndPublish(content); + var model = new ContentItemSaveBuilder() + .WithContent(content) + .Build(); + + + // Act + var response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + // Assert + + var body = await response.Content.ReadAsStringAsync(); + + body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix); + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, body); + var display = JsonConvert.DeserializeObject(body); + Assert.AreEqual(1, display.Variants.Count()); + } + + [Test] + public async Task PostSave_Validate_Empty_Name() + { + var url = PrepareUrl(x => x.PostSave(null)); + + var contentService = GetRequiredService(); + var contentTypeService = GetRequiredService(); + + var contentType = new ContentTypeBuilder() + .WithId(0) + .AddPropertyType() + .WithAlias("title") + .WithValueStorageType(ValueStorageType.Integer) + .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox) + .WithName("Title") + .Done() + .WithContentVariation(ContentVariation.Nothing) + .Build(); + + contentTypeService.Save(contentType); + + var content = new ContentBuilder() + .WithId(0) + .WithName("Invariant") + .WithContentType(contentType) + .AddPropertyData() + .WithKeyValue("title", "Cool invariant title") + .Done() + .Build(); + contentService.SaveAndPublish(content); + + content.Name = null; // Removes the name of one of the variants to force an error + var model = new ContentItemSaveBuilder() + .WithContent(content) + .Build(); + + + // Act + var response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + // Assert + + var body = await response.Content.ReadAsStringAsync(); + + body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix); + + Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); + var display = JsonConvert.DeserializeObject(body); + Assert.AreEqual(1, display.Errors.Count()); + Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); + //ModelState":{"Variants[0].Name":["Required"]} + } + + [Test] + public async Task PostSave_Validate_Variants_Empty_Name() + { + var url = PrepareUrl(x => x.PostSave(null)); + + var contentService = GetRequiredService(); + var contentTypeService = GetRequiredService(); + + var contentType = new ContentTypeBuilder() + .WithId(0) + .AddPropertyType() + .WithAlias("title") + .WithValueStorageType(ValueStorageType.Integer) + .WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox) + .WithName("Title") + .Done() + .WithContentVariation(ContentVariation.Culture) + .Build(); + + contentTypeService.Save(contentType); + + var content = new ContentBuilder() + .WithId(0) + .WithCultureName("en-US", "English") + .WithCultureName("da-DK", "Danish") + .WithContentType(contentType) + .AddPropertyData() + .WithKeyValue("title", "Cool invariant title") + .Done() + .Build(); + contentService.SaveAndPublish(content); + + content.CultureInfos[0].Name = null; // Removes the name of one of the variants to force an error + var model = new ContentItemSaveBuilder() + .WithContent(content) + .Build(); + + // Act + var response = await Client.PostAsync(url, new MultipartFormDataContent + { + { new StringContent(JsonConvert.SerializeObject(model)), "contentItem" } + }); + + // Assert + + var body = await response.Content.ReadAsStringAsync(); + + body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix); + Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); + var display = JsonConvert.DeserializeObject(body); + Assert.AreEqual(2, display.Errors.Count()); + Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); + Assert.IsTrue(display.Errors.ContainsKey("_content_variant_en-US_null_")); + } + + + private class TestContentItemSave : ContentItemSave + { + } + } +} diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs new file mode 100644 index 0000000000..a0d3d65e94 --- /dev/null +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -0,0 +1,76 @@ + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Reflection; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using Umbraco.Composing; +using Umbraco.Extensions; +using Umbraco.Tests.Integration.Testing; +using Umbraco.Web; +using Umbraco.Web.Common.Controllers; +using Umbraco.Web.Editors; + + +namespace Umbraco.Tests.Integration.TestServerTest +{ + [TestFixture] + public abstract class UmbracoTestServerTestBase : UmbracoIntegrationTest + { + [SetUp] + public void SetUp() + { + Factory = new UmbracoWebApplicationFactory(TestDBConnectionString); + Client = Factory.CreateClient(new WebApplicationFactoryClientOptions(){ + AllowAutoRedirect = false + }); + LinkGenerator = Factory.Services.GetRequiredService(); + + } + + protected T GetRequiredService() => Factory.Services.GetRequiredService(); + protected string PrepareUrl(Expression> methodSelector) + where T : UmbracoApiController + { + var url = LinkGenerator.GetUmbracoApiService(methodSelector); + + var umbracoContextFactory = GetRequiredService(); + var httpContextAccessor = GetRequiredService(); + + httpContextAccessor.HttpContext = new DefaultHttpContext + { + Request = + { + Scheme = "https", + Host = new HostString("localhost", 80), + Path = url, + QueryString = new QueryString(string.Empty) + } + }; + + umbracoContextFactory.EnsureUmbracoContext(); + + return url; + } + + protected HttpClient Client { get; set; } + protected LinkGenerator LinkGenerator { get; set; } + protected UmbracoWebApplicationFactory Factory { get; set; } + + [TearDown] + public void TearDown() + { + Client.Dispose(); + + if (Current.IsInitialized) + { + typeof(Current).GetProperty(nameof(Current.IsInitialized), BindingFlags.Public | BindingFlags.Static).SetValue(null, false); + } + + } + } +} diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs new file mode 100644 index 0000000000..d2f95a42cf --- /dev/null +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Core; +using Umbraco.Core.BackOffice; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Mapping; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Common.Security; +using Umbraco.Web.UI.BackOffice; + +namespace Umbraco.Tests.Integration.TestServerTest +{ + public class UmbracoWebApplicationFactory : CustomWebApplicationFactory + { + public UmbracoWebApplicationFactory(string testDbConnectionString) :base(testDbConnectionString) + { + } + } + + public abstract class CustomWebApplicationFactory : WebApplicationFactory where TStartup : class + { + private readonly string _testDbConnectionString; + + protected CustomWebApplicationFactory(string testDbConnectionString) + { + _testDbConnectionString = testDbConnectionString; + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + base.ConfigureWebHost(builder); + + builder.ConfigureTestServices(services => + { + services.AddAuthentication("Test").AddScheme("Test", options => {}); + }); + + builder.ConfigureAppConfiguration(x => + { + x.AddInMemoryCollection(new Dictionary() + { + ["ConnectionStrings:"+ Constants.System.UmbracoConnectionName] = _testDbConnectionString + }); + }); + + } + + protected override IHostBuilder CreateHostBuilder() + { + + var builder = base.CreateHostBuilder(); + builder.UseUmbraco(); + return builder; + } + } + + public class TestAuthHandler : AuthenticationHandler + { + private readonly BackOfficeSignInManager _backOfficeSignInManager; + + private readonly BackOfficeIdentityUser _fakeUser; + public TestAuthHandler(IOptionsMonitor options, + ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, BackOfficeSignInManager backOfficeSignInManager, IUserService userService, UmbracoMapper umbracoMapper) + : base(options, logger, encoder, clock) + { + _backOfficeSignInManager = backOfficeSignInManager; + + var user = userService.GetUserById(Constants.Security.SuperUserId); + _fakeUser = umbracoMapper.Map(user); + _fakeUser.SecurityStamp = "Needed"; + } + + protected override async Task HandleAuthenticateAsync() + { + + var principal = await _backOfficeSignInManager.CreateUserPrincipalAsync(_fakeUser); + var ticket = new AuthenticationTicket(principal, "Test"); + + return AuthenticateResult.Success(ticket); + } + } +} diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 372afbecd2..26df0bbfb0 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -126,7 +126,8 @@ namespace Umbraco.Tests.Integration.Testing Services.GetRequiredService().EnsureUmbracoContext(); // This will create a db, install the schema and ensure the app is configured to run - app.UseTestLocalDb(testHelper.WorkingDirectory, this); + app.UseTestLocalDb(testHelper.WorkingDirectory, this, out var connectionString); + TestDBConnectionString = connectionString; app.UseUmbracoCore(); @@ -134,6 +135,8 @@ namespace Umbraco.Tests.Integration.Testing #region Common services + protected string TestDBConnectionString { get; private set; } + protected virtual Action CustomTestSetup => services => { }; /// diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Models/LanguageTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Models/LanguageTests.cs index 4dcf1a7a97..eb2ae7235a 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Models/LanguageTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Models/LanguageTests.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Tests.Common.Builders; +using Umbraco.Tests.Common.Builders.Extensions; namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Models { @@ -19,7 +20,9 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Infrastructure.Models [Test] public void Can_Deep_Clone() { - var item = _builder.Build(); + var item = _builder + .WithId(1) + .Build(); var clone = (Language) item.DeepClone(); Assert.AreNotSame(clone, item); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.Common/Builders/LanguageBuilderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.Common/Builders/LanguageBuilderTests.cs index a6ac5b9a21..b4dfaef6e3 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.Common/Builders/LanguageBuilderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.Common/Builders/LanguageBuilderTests.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System.Globalization; +using NUnit.Framework; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Common.Builders.Extensions; @@ -13,14 +14,15 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Tests.Common.Builders // Arrange var builder = new LanguageBuilder(); + var expected = CultureInfo.GetCultureInfo("en-GB"); // Act var language = builder - .WithCultureInfo("en-GB") + .WithCultureInfo(expected.Name) .Build(); // Assert - Assert.AreEqual("GB", language.IsoCode); - Assert.AreEqual("en", language.CultureName); + Assert.AreEqual(expected.Name, language.IsoCode); + Assert.AreEqual(expected.EnglishName, language.CultureName); } } } diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerUnitTests.cs similarity index 99% rename from src/Umbraco.Tests/Web/Controllers/ContentControllerUnitTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerUnitTests.cs index 5697e2bc7f..56f52215a2 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerUnitTests.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Web.Http; using Moq; using NUnit.Framework; using Umbraco.Core.Models; @@ -7,14 +6,13 @@ using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Web.Editors; +using Umbraco.Tests.Common.TestHelpers.Entities; namespace Umbraco.Tests.Web.Controllers { [TestFixture] public class ContentControllerUnitTests - { + { [Test] public void Access_Allowed_By_Path() diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 4f0e5e1810..a2ebdd5e66 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -295,7 +295,6 @@ - diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index 55629fde62..b92db6d4ee 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -1,518 +1,518 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Web.Http; -using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Dictionary; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.ControllerTesting; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Tests.Testing; -using Umbraco.Web; -using Umbraco.Web.Actions; -using Umbraco.Web.Editors; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.PropertyEditors; -using Umbraco.Web.Trees; -using Umbraco.Web.WebApi; -using Umbraco.Web.Composing; -using Task = System.Threading.Tasks.Task; -using Umbraco.Core.Mapping; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Web.Controllers -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.None)] - public class ContentControllerTests : TestWithDatabaseBase - { - private IContentType _contentTypeForMockedContent; - - public override void SetUp() - { - base.SetUp(); - _contentTypeForMockedContent = null; - } - - protected override void ComposeApplication(bool withApplication) - { - base.ComposeApplication(withApplication); - - //Replace with mockable services: - - var userServiceMock = new Mock(); - userServiceMock.Setup(service => service.GetUserById(It.IsAny())) - .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", null, new List(), new int[0], new int[0]) : null); - userServiceMock.Setup(x => x.GetProfileById(It.IsAny())) - .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", null, new List(), new int[0], new int[0]) : null); - userServiceMock.Setup(service => service.GetPermissionsForPath(It.IsAny(), It.IsAny())) - .Returns(new EntityPermissionSet(123, new EntityPermissionCollection(new[] - { - new EntityPermission(0, 123, new[] - { - ActionBrowse.ActionLetter.ToString(), - ActionUpdate.ActionLetter.ToString(), - ActionPublish.ActionLetter.ToString(), - ActionNew.ActionLetter.ToString() - }), - }))); - - var entityService = new Mock(); - entityService.Setup(x => x.GetAllPaths(UmbracoObjectTypes.Document, It.IsAny())) - .Returns((UmbracoObjectTypes objType, int[] ids) => ids.Select(x => new TreeEntityPath { Path = $"-1,{x}", Id = x }).ToList()); - entityService.Setup(x => x.GetKey(It.IsAny(), UmbracoObjectTypes.DataType)) - .Returns((int id, UmbracoObjectTypes objType) => - { - switch (id) - { - case Constants.DataTypes.Textbox: - return Attempt.Succeed(Constants.DataTypes.Guids.TextstringGuid); - case Constants.DataTypes.RichtextEditor: - return Attempt.Succeed(Constants.DataTypes.Guids.RichtextEditorGuid); - } - return Attempt.Fail(); - }); - - - var dataTypeService = new Mock(); - dataTypeService.Setup(service => service.GetDataType(It.IsAny())) - .Returns(Mock.Of(type => type.Id == 9876 && type.Name == "text")); - dataTypeService.Setup(service => service.GetDataType(-87)) //the RTE - .Returns(Mock.Of(type => type.Id == -87 && type.Name == "Rich text" && type.Configuration == new RichTextConfiguration())); - - var langService = new Mock(); - langService.Setup(x => x.GetAllLanguages()).Returns(new[] { - Mock.Of(x => x.IsoCode == "en-US"), - Mock.Of(x => x.IsoCode == "es-ES"), - Mock.Of(x => x.IsoCode == "fr-FR") - }); - - var textService = new Mock(); - textService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())).Returns("text"); - - Composition.RegisterUnique(f => Mock.Of()); - Composition.RegisterUnique(f => Mock.Of()); - Composition.RegisterUnique(f => userServiceMock.Object); - Composition.RegisterUnique(f => entityService.Object); - Composition.RegisterUnique(f => dataTypeService.Object); - Composition.RegisterUnique(f => langService.Object); - Composition.RegisterUnique(f => textService.Object); - Composition.RegisterUnique(f => Mock.Of()); - Composition.RegisterUnique(f => new UmbracoApiControllerTypeCollection(new Type[] { })); - } - - private MultipartFormDataContent GetMultiPartRequestContent(string json) - { - var multiPartBoundary = "----WebKitFormBoundary123456789"; - return new MultipartFormDataContent(multiPartBoundary) - { - new StringContent(json) - { - Headers = - { - ContentDisposition = new ContentDispositionHeaderValue("form-data") - { - Name = "contentItem" - } - } - } - }; - } - - private IContentType GetMockedContentType() - { - var contentType = MockedContentTypes.CreateSimpleContentType(); - //ensure things have ids - var ids = 888; - foreach (var g in contentType.CompositionPropertyGroups) - { - g.Id = ids; - ids++; - } - foreach (var p in contentType.CompositionPropertyGroups) - { - p.Id = ids; - ids++; - } - - return contentType; - } - - private IContent GetMockedContent() - { - if (_contentTypeForMockedContent == null) - { - _contentTypeForMockedContent = GetMockedContentType(); - Mock.Get(ServiceContext.ContentTypeService) - .Setup(x => x.Get(_contentTypeForMockedContent.Id)) - .Returns(_contentTypeForMockedContent); - Mock.Get(ServiceContext.ContentTypeService) - .As() - .Setup(x => x.Get(_contentTypeForMockedContent.Id)) - .Returns(_contentTypeForMockedContent); - } - - var content = MockedContent.CreateSimpleContent(_contentTypeForMockedContent); - content.Id = 123; - content.Path = "-1,123"; - return content; - } - - private const string PublishJsonInvariant = @"{ - ""id"": 123, - ""contentTypeAlias"": ""page"", - ""parentId"": -1, - ""action"": ""save"", - ""variants"": [ - { - ""name"": ""asdf"", - ""properties"": [ - { - ""id"": 1, - ""alias"": ""title"", - ""value"": ""asdf"" - } - ], - ""culture"": null, - ""save"": true, - ""publish"": true - } - ] -}"; - - private const string PublishJsonVariant = @"{ - ""id"": 123, - ""contentTypeAlias"": ""page"", - ""parentId"": -1, - ""action"": ""save"", - ""variants"": [ - { - ""name"": ""asdf"", - ""properties"": [ - { - ""id"": 1, - ""alias"": ""title"", - ""value"": ""asdf"" - } - ], - ""culture"": ""en-US"", - ""save"": true, - ""publish"": true - }, - { - ""name"": ""asdf"", - ""properties"": [ - { - ""id"": 1, - ""alias"": ""title"", - ""value"": ""asdf"" - } - ], - ""culture"": ""fr-FR"", - ""save"": true, - ""publish"": true - }, - { - ""name"": ""asdf"", - ""properties"": [ - { - ""id"": 1, - ""alias"": ""title"", - ""value"": ""asdf"" - } - ], - ""culture"": ""es-ES"", - ""save"": true, - ""publish"": true - } - ] -}"; - - /// - /// Returns 404 if the content wasn't found based on the ID specified - /// - /// - [Test] - public async Task PostSave_Validate_Existing_Content() - { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) - { - var contentServiceMock = Mock.Get(ServiceContext.ContentService); - contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null); //do not find it - - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - - var controller = new ContentController( - Factory.GetInstance(), - propertyEditorCollection, - Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - ShortStringHelper, - Factory.GetInstance(), - Factory.GetInstance()); - - return controller; - } - - var runner = new TestRunner(CtrlFactory); - var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, - content: GetMultiPartRequestContent(PublishJsonInvariant), - mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), - assertOkResponse: false); - - Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); - Assert.AreEqual(")]}',\n{\"Message\":\"content was not found\"}", response.Item1.Content.ReadAsStringAsync().Result); - - //var obj = JsonConvert.DeserializeObject>(response.Item2); - //Assert.AreEqual(0, obj.TotalItems); - } - - [Test] - public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving() - { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) - { - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var controller = new ContentController( - Factory.GetInstance(), - propertyEditorCollection, - Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - ShortStringHelper, - Factory.GetInstance(), - Factory.GetInstance()); - - return controller; - } - - var json = JsonConvert.DeserializeObject(PublishJsonInvariant); - //remove all save flaggs - ((JArray)json["variants"])[0]["save"] = false; - - var runner = new TestRunner(CtrlFactory); - var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, - content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), - mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), - assertOkResponse: false); - - Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); - Assert.AreEqual(")]}',\n{\"Message\":\"No variants flagged for saving\"}", response.Item1.Content.ReadAsStringAsync().Result); - } - - /// - /// Returns 404 if any of the posted properties dont actually exist - /// - /// - [Test] - public async Task PostSave_Validate_Properties_Exist() - { - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) - { - var contentServiceMock = Mock.Get(ServiceContext.ContentService); - contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); - - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var controller = new ContentController( - Factory.GetInstance(), - propertyEditorCollection, - Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - ShortStringHelper, - Factory.GetInstance(), - Factory.GetInstance()); - - return controller; - } - - var json = JsonConvert.DeserializeObject(PublishJsonInvariant); - //add a non-existent property to a variant being saved - var variantProps = (JArray)json["variants"].ElementAt(0)["properties"]; - variantProps.Add(JObject.FromObject(new - { - id = 2, - alias = "doesntExist", - value = "hello" - })); - - var runner = new TestRunner(CtrlFactory); - var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, - content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), - mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), - assertOkResponse: false); - - Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); - } - - [Test] - public async Task PostSave_Simple_Invariant() - { - var content = GetMockedContent(); - - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) - { - var contentServiceMock = Mock.Get(ServiceContext.ContentService); - contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); - contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success - - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var controller = new ContentController( - Factory.GetInstance(), - propertyEditorCollection, - Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - ShortStringHelper, - Factory.GetInstance(), - Factory.GetInstance()); - - return controller; - } - - var runner = new TestRunner(CtrlFactory); - var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, - content: GetMultiPartRequestContent(PublishJsonInvariant), - mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), - assertOkResponse: false); - - Assert.AreEqual(HttpStatusCode.OK, response.Item1.StatusCode); - var display = JsonConvert.DeserializeObject(response.Item2); - Assert.AreEqual(1, display.Variants.Count()); - } - - [Test] - public async Task PostSave_Validate_Empty_Name() - { - var content = GetMockedContent(); - - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) - { - var contentServiceMock = Mock.Get(ServiceContext.ContentService); - contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); - contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success - - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var controller = new ContentController( - Factory.GetInstance(), - propertyEditorCollection, - Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - ShortStringHelper, - Factory.GetInstance(), - Factory.GetInstance() - ); - - return controller; - } - - //clear out the name - var json = JsonConvert.DeserializeObject(PublishJsonInvariant); - json["variants"].ElementAt(0)["name"] = null; - - var runner = new TestRunner(CtrlFactory); - var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, - content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), - mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), - assertOkResponse: false); - - Assert.AreEqual(HttpStatusCode.BadRequest, response.Item1.StatusCode); - var display = JsonConvert.DeserializeObject(response.Item2); - Assert.AreEqual(1, display.Errors.Count()); - Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); - //ModelState":{"Variants[0].Name":["Required"]} - } - - [Test] - public async Task PostSave_Validate_Variants_Empty_Name() - { - var content = GetMockedContent(); - - ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) - { - var contentServiceMock = Mock.Get(ServiceContext.ContentService); - contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); - contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success - - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var controller = new ContentController( - Factory.GetInstance(), - propertyEditorCollection, - Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - ShortStringHelper, - Factory.GetInstance(), - Factory.GetInstance() - ); - - return controller; - } - - //clear out one of the names - var json = JsonConvert.DeserializeObject(PublishJsonVariant); - json["variants"].ElementAt(0)["name"] = null; - - var runner = new TestRunner(CtrlFactory); - var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, - content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), - mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), - assertOkResponse: false); - - Assert.AreEqual(HttpStatusCode.BadRequest, response.Item1.StatusCode); - var display = JsonConvert.DeserializeObject(response.Item2); - Assert.AreEqual(2, display.Errors.Count()); - Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); - Assert.IsTrue(display.Errors.ContainsKey("_content_variant_en-US_null_")); - } - - // TODO: There are SOOOOO many more tests we should write - a lot of them to do with validation - - } -} +// using System; +// using System.Collections.Generic; +// using System.Globalization; +// using System.Linq; +// using System.Net; +// using System.Net.Http; +// using System.Net.Http.Headers; +// using System.Web.Http; +// using Moq; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// using NUnit.Framework; +// using Umbraco.Core; +// using Umbraco.Core.Cache; +// using Umbraco.Core.Configuration; +// using Umbraco.Core.Dictionary; +// using Umbraco.Core.Logging; +// using Umbraco.Core.Models; +// using Umbraco.Core.Models.Entities; +// using Umbraco.Core.Models.Membership; +// using Umbraco.Core.Persistence; +// using Umbraco.Core.PropertyEditors; +// using Umbraco.Core.Services; +// using Umbraco.Tests.TestHelpers; +// using Umbraco.Tests.TestHelpers.ControllerTesting; +// using Umbraco.Tests.TestHelpers.Entities; +// using Umbraco.Tests.Testing; +// using Umbraco.Web; +// using Umbraco.Web.Actions; +// using Umbraco.Web.Editors; +// using Umbraco.Web.Models.ContentEditing; +// using Umbraco.Web.PropertyEditors; +// using Umbraco.Web.Trees; +// using Umbraco.Web.WebApi; +// using Umbraco.Web.Composing; +// using Task = System.Threading.Tasks.Task; +// using Umbraco.Core.Mapping; +// using Umbraco.Web.Routing; +// +// namespace Umbraco.Tests.Web.Controllers +// { +// [TestFixture] +// [UmbracoTest(Database = UmbracoTestOptions.Database.None)] +// public class ContentControllerTests : TestWithDatabaseBase +// { +// private IContentType _contentTypeForMockedContent; +// +// public override void SetUp() +// { +// base.SetUp(); +// _contentTypeForMockedContent = null; +// } +// +// protected override void ComposeApplication(bool withApplication) +// { +// base.ComposeApplication(withApplication); +// +// //Replace with mockable services: +// +// var userServiceMock = new Mock(); +// userServiceMock.Setup(service => service.GetUserById(It.IsAny())) +// .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", null, new List(), new int[0], new int[0]) : null); +// userServiceMock.Setup(x => x.GetProfileById(It.IsAny())) +// .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", null, new List(), new int[0], new int[0]) : null); +// userServiceMock.Setup(service => service.GetPermissionsForPath(It.IsAny(), It.IsAny())) +// .Returns(new EntityPermissionSet(123, new EntityPermissionCollection(new[] +// { +// new EntityPermission(0, 123, new[] +// { +// ActionBrowse.ActionLetter.ToString(), +// ActionUpdate.ActionLetter.ToString(), +// ActionPublish.ActionLetter.ToString(), +// ActionNew.ActionLetter.ToString() +// }), +// }))); +// +// var entityService = new Mock(); +// entityService.Setup(x => x.GetAllPaths(UmbracoObjectTypes.Document, It.IsAny())) +// .Returns((UmbracoObjectTypes objType, int[] ids) => ids.Select(x => new TreeEntityPath { Path = $"-1,{x}", Id = x }).ToList()); +// entityService.Setup(x => x.GetKey(It.IsAny(), UmbracoObjectTypes.DataType)) +// .Returns((int id, UmbracoObjectTypes objType) => +// { +// switch (id) +// { +// case Constants.DataTypes.Textbox: +// return Attempt.Succeed(Constants.DataTypes.Guids.TextstringGuid); +// case Constants.DataTypes.RichtextEditor: +// return Attempt.Succeed(Constants.DataTypes.Guids.RichtextEditorGuid); +// } +// return Attempt.Fail(); +// }); +// +// +// var dataTypeService = new Mock(); +// dataTypeService.Setup(service => service.GetDataType(It.IsAny())) +// .Returns(Mock.Of(type => type.Id == 9876 && type.Name == "text")); +// dataTypeService.Setup(service => service.GetDataType(-87)) //the RTE +// .Returns(Mock.Of(type => type.Id == -87 && type.Name == "Rich text" && type.Configuration == new RichTextConfiguration())); +// +// var langService = new Mock(); +// langService.Setup(x => x.GetAllLanguages()).Returns(new[] { +// Mock.Of(x => x.IsoCode == "en-US"), +// Mock.Of(x => x.IsoCode == "es-ES"), +// Mock.Of(x => x.IsoCode == "fr-FR") +// }); +// +// var textService = new Mock(); +// textService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())).Returns("text"); +// +// Composition.RegisterUnique(f => Mock.Of()); +// Composition.RegisterUnique(f => Mock.Of()); +// Composition.RegisterUnique(f => userServiceMock.Object); +// Composition.RegisterUnique(f => entityService.Object); +// Composition.RegisterUnique(f => dataTypeService.Object); +// Composition.RegisterUnique(f => langService.Object); +// Composition.RegisterUnique(f => textService.Object); +// Composition.RegisterUnique(f => Mock.Of()); +// Composition.RegisterUnique(f => new UmbracoApiControllerTypeCollection(new Type[] { })); +// } +// +// private MultipartFormDataContent GetMultiPartRequestContent(string json) +// { +// var multiPartBoundary = "----WebKitFormBoundary123456789"; +// return new MultipartFormDataContent(multiPartBoundary) +// { +// new StringContent(json) +// { +// Headers = +// { +// ContentDisposition = new ContentDispositionHeaderValue("form-data") +// { +// Name = "contentItem" +// } +// } +// } +// }; +// } +// +// private IContentType GetMockedContentType() +// { +// var contentType = MockedContentTypes.CreateSimpleContentType(); +// //ensure things have ids +// var ids = 888; +// foreach (var g in contentType.CompositionPropertyGroups) +// { +// g.Id = ids; +// ids++; +// } +// foreach (var p in contentType.CompositionPropertyGroups) +// { +// p.Id = ids; +// ids++; +// } +// +// return contentType; +// } +// +// private IContent GetMockedContent() +// { +// if (_contentTypeForMockedContent == null) +// { +// _contentTypeForMockedContent = GetMockedContentType(); +// Mock.Get(ServiceContext.ContentTypeService) +// .Setup(x => x.Get(_contentTypeForMockedContent.Id)) +// .Returns(_contentTypeForMockedContent); +// Mock.Get(ServiceContext.ContentTypeService) +// .As() +// .Setup(x => x.Get(_contentTypeForMockedContent.Id)) +// .Returns(_contentTypeForMockedContent); +// } +// +// var content = MockedContent.CreateSimpleContent(_contentTypeForMockedContent); +// content.Id = 123; +// content.Path = "-1,123"; +// return content; +// } +// +// private const string PublishJsonInvariant = @"{ +// ""id"": 123, +// ""contentTypeAlias"": ""page"", +// ""parentId"": -1, +// ""action"": ""save"", +// ""variants"": [ +// { +// ""name"": ""asdf"", +// ""properties"": [ +// { +// ""id"": 1, +// ""alias"": ""title"", +// ""value"": ""asdf"" +// } +// ], +// ""culture"": null, +// ""save"": true, +// ""publish"": true +// } +// ] +// }"; +// +// private const string PublishJsonVariant = @"{ +// ""id"": 123, +// ""contentTypeAlias"": ""page"", +// ""parentId"": -1, +// ""action"": ""save"", +// ""variants"": [ +// { +// ""name"": ""asdf"", +// ""properties"": [ +// { +// ""id"": 1, +// ""alias"": ""title"", +// ""value"": ""asdf"" +// } +// ], +// ""culture"": ""en-US"", +// ""save"": true, +// ""publish"": true +// }, +// { +// ""name"": ""asdf"", +// ""properties"": [ +// { +// ""id"": 1, +// ""alias"": ""title"", +// ""value"": ""asdf"" +// } +// ], +// ""culture"": ""fr-FR"", +// ""save"": true, +// ""publish"": true +// }, +// { +// ""name"": ""asdf"", +// ""properties"": [ +// { +// ""id"": 1, +// ""alias"": ""title"", +// ""value"": ""asdf"" +// } +// ], +// ""culture"": ""es-ES"", +// ""save"": true, +// ""publish"": true +// } +// ] +// }"; +// +// /// +// /// Returns 404 if the content wasn't found based on the ID specified +// /// +// /// +// [Test] +// public async Task PostSave_Validate_Existing_Content() +// { +// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) +// { +// var contentServiceMock = Mock.Get(ServiceContext.ContentService); +// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null); //do not find it +// +// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); +// +// var controller = new ContentController( +// Factory.GetInstance(), +// propertyEditorCollection, +// Factory.GetInstance(), +// umbracoContextAccessor, +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// ShortStringHelper, +// Factory.GetInstance(), +// Factory.GetInstance()); +// +// return controller; +// } +// +// var runner = new TestRunner(CtrlFactory); +// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, +// content: GetMultiPartRequestContent(PublishJsonInvariant), +// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), +// assertOkResponse: false); +// +// Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); +// Assert.AreEqual(")]}',\n{\"Message\":\"content was not found\"}", response.Item1.Content.ReadAsStringAsync().Result); +// +// //var obj = JsonConvert.DeserializeObject>(response.Item2); +// //Assert.AreEqual(0, obj.TotalItems); +// } +// +// [Test] +// public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving() +// { +// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) +// { +// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); +// var controller = new ContentController( +// Factory.GetInstance(), +// propertyEditorCollection, +// Factory.GetInstance(), +// umbracoContextAccessor, +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// ShortStringHelper, +// Factory.GetInstance(), +// Factory.GetInstance()); +// +// return controller; +// } +// +// var json = JsonConvert.DeserializeObject(PublishJsonInvariant); +// //remove all save flaggs +// ((JArray)json["variants"])[0]["save"] = false; +// +// var runner = new TestRunner(CtrlFactory); +// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, +// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), +// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), +// assertOkResponse: false); +// +// Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); +// Assert.AreEqual(")]}',\n{\"Message\":\"No variants flagged for saving\"}", response.Item1.Content.ReadAsStringAsync().Result); +// } +// +// /// +// /// Returns 404 if any of the posted properties dont actually exist +// /// +// /// +// [Test] +// public async Task PostSave_Validate_Properties_Exist() +// { +// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) +// { +// var contentServiceMock = Mock.Get(ServiceContext.ContentService); +// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); +// +// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); +// var controller = new ContentController( +// Factory.GetInstance(), +// propertyEditorCollection, +// Factory.GetInstance(), +// umbracoContextAccessor, +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// ShortStringHelper, +// Factory.GetInstance(), +// Factory.GetInstance()); +// +// return controller; +// } +// +// var json = JsonConvert.DeserializeObject(PublishJsonInvariant); +// //add a non-existent property to a variant being saved +// var variantProps = (JArray)json["variants"].ElementAt(0)["properties"]; +// variantProps.Add(JObject.FromObject(new +// { +// id = 2, +// alias = "doesntExist", +// value = "hello" +// })); +// +// var runner = new TestRunner(CtrlFactory); +// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, +// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), +// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), +// assertOkResponse: false); +// +// Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); +// } +// +// [Test] +// public async Task PostSave_Simple_Invariant() +// { +// var content = GetMockedContent(); +// +// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) +// { +// var contentServiceMock = Mock.Get(ServiceContext.ContentService); +// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); +// contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) +// .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success +// +// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); +// var controller = new ContentController( +// Factory.GetInstance(), +// propertyEditorCollection, +// Factory.GetInstance(), +// umbracoContextAccessor, +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// ShortStringHelper, +// Factory.GetInstance(), +// Factory.GetInstance()); +// +// return controller; +// } +// +// var runner = new TestRunner(CtrlFactory); +// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, +// content: GetMultiPartRequestContent(PublishJsonInvariant), +// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), +// assertOkResponse: false); +// +// Assert.AreEqual(HttpStatusCode.OK, response.Item1.StatusCode); +// var display = JsonConvert.DeserializeObject(response.Item2); +// Assert.AreEqual(1, display.Variants.Count()); +// } +// +// [Test] +// public async Task PostSave_Validate_Empty_Name() +// { +// var content = GetMockedContent(); +// +// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) +// { +// var contentServiceMock = Mock.Get(ServiceContext.ContentService); +// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); +// contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) +// .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success +// +// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); +// var controller = new ContentController( +// Factory.GetInstance(), +// propertyEditorCollection, +// Factory.GetInstance(), +// umbracoContextAccessor, +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// ShortStringHelper, +// Factory.GetInstance(), +// Factory.GetInstance() +// ); +// +// return controller; +// } +// +// //clear out the name +// var json = JsonConvert.DeserializeObject(PublishJsonInvariant); +// json["variants"].ElementAt(0)["name"] = null; +// +// var runner = new TestRunner(CtrlFactory); +// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, +// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), +// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), +// assertOkResponse: false); +// +// Assert.AreEqual(HttpStatusCode.BadRequest, response.Item1.StatusCode); +// var display = JsonConvert.DeserializeObject(response.Item2); +// Assert.AreEqual(1, display.Errors.Count()); +// Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); +// //ModelState":{"Variants[0].Name":["Required"]} +// } +// +// [Test] +// public async Task PostSave_Validate_Variants_Empty_Name() +// { +// var content = GetMockedContent(); +// +// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) +// { +// var contentServiceMock = Mock.Get(ServiceContext.ContentService); +// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); +// contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) +// .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success +// +// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); +// var controller = new ContentController( +// Factory.GetInstance(), +// propertyEditorCollection, +// Factory.GetInstance(), +// umbracoContextAccessor, +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// Factory.GetInstance(), +// ShortStringHelper, +// Factory.GetInstance(), +// Factory.GetInstance() +// ); +// +// return controller; +// } +// +// //clear out one of the names +// var json = JsonConvert.DeserializeObject(PublishJsonVariant); +// json["variants"].ElementAt(0)["name"] = null; +// +// var runner = new TestRunner(CtrlFactory); +// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, +// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), +// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), +// assertOkResponse: false); +// +// Assert.AreEqual(HttpStatusCode.BadRequest, response.Item1.StatusCode); +// var display = JsonConvert.DeserializeObject(response.Item2); +// Assert.AreEqual(2, display.Errors.Count()); +// Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); +// Assert.IsTrue(display.Errors.ContainsKey("_content_variant_en-US_null_")); +// } +// +// // TODO: There are SOOOOO many more tests we should write - a lot of them to do with validation +// +// } +// } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs index a4c115f66f..0bea7a257e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs @@ -9,6 +9,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// currently in the request. /// [TypeFilter(typeof(AppendCurrentEventMessagesAttribute))] + //[PrefixlessBodyModelValidator] // TODO implement this!! public abstract class BackOfficeNotificationsController : UmbracoAuthorizedJsonController { diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index d51c6fd0bf..ccc33b1f91 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -488,23 +488,7 @@ namespace Umbraco.Web.Editors /// /// [FilterAllowedOutgoingContent(typeof(IEnumerable>), "Items")] - public PagedResult> GetChildren( - int id, - int pageNumber = 0, // TODO: This should be '1' as it's not the index - int pageSize = 0, - string orderBy = "SortOrder", - Direction orderDirection = Direction.Ascending, - bool orderBySystemField = true, - string filter = "") - { - return GetChildren(id, null, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); - } - - /// - /// Gets the children for the content id passed in - /// - /// - [FilterAllowedOutgoingContent(typeof(IEnumerable>), "Items")] + [DetermineAmbiguousActionByPassingParameters] public PagedResult> GetChildren( int id, string includeProperties, diff --git a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs index b206cbde01..caa3b8348a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.BackOffice.Controllers var type = parameterDescriptor.ParameterType; - if(typeof(IEnumerable).IsAssignableFrom(type)) + if(typeof(IEnumerable).IsAssignableFrom(type) && typeof(string) != type) { type = type.GetElementType(); } @@ -54,6 +54,14 @@ namespace Umbraco.Web.BackOffice.Controllers { canUse &= true; } + else if (type == typeof(bool)) + { + canUse &= bool.TryParse(value, out _); + } + else if (type == typeof(Direction)) + { + canUse &= Enum.TryParse(value, out _); + } else { canUse &= true; diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 9a9d22a854..05ba154384 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.Infrastructure; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -22,8 +23,10 @@ namespace Umbraco.Web.BackOffice.Filters { public ContentSaveValidationAttribute() : base(typeof(ContentSaveValidationFilter)) { + Order = -3000; // More important than ModelStateInvalidFilter.FilterOrder } + private sealed class ContentSaveValidationFilter : IActionFilter { private readonly IContentService _contentService; @@ -79,7 +82,7 @@ namespace Umbraco.Web.BackOffice.Filters { if (!contentItem.Variants.Any(x => x.Save)) { - actionContext.Result = new NotFoundObjectResult("No variants flagged for saving"); + actionContext.Result = new NotFoundObjectResult(new {Message = "No variants flagged for saving"}); return false; } diff --git a/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs b/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs index bbd9d5207a..ac23516b9b 100644 --- a/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs +++ b/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Common.ApplicationModels ActionModelConventions = new List() { new ClientErrorResultFilterConvention(), // Ensures the responses without any body is converted into a simple json object with info instead of a string like "Status Code: 404; Not Found" - new InvalidModelStateFilterConvention(), // automatically 400 responses if ModelState is invalid before hitting the controller + //new InvalidModelStateFilterConvention(), // automatically 400 responses if ModelState is invalid before hitting the controller new ConsumesConstraintForFormFileParameterConvention(), // If an controller accepts files, it must accept multipart/form-data. new InferParameterBindingInfoConvention(modelMetadataProvider), // no need for [FromBody] everywhere, A complex type parameter is assigned to FromBody diff --git a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs index 9c86c85b5c..4419015105 100644 --- a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs +++ b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs @@ -135,7 +135,7 @@ namespace Umbraco.Web get { //NOTE: the request can be null during app startup! - return Current.HostingEnvironment.IsDebugMode + return _hostingEnvironment.IsDebugMode && (string.IsNullOrEmpty(_requestAccessor.GetRequestValue("umbdebugshowtrace")) == false || string.IsNullOrEmpty(_requestAccessor.GetRequestValue("umbdebug")) == false || string.IsNullOrEmpty(_cookieManager.GetCookieValue("UMB-DEBUG")) == false);