diff --git a/src/Umbraco.Core/BackOffice/IdentityMapDefinition.cs b/src/Umbraco.Core/BackOffice/IdentityMapDefinition.cs index b2702c1ddb..523876edc3 100644 --- a/src/Umbraco.Core/BackOffice/IdentityMapDefinition.cs +++ b/src/Umbraco.Core/BackOffice/IdentityMapDefinition.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Options; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; @@ -43,14 +43,14 @@ namespace Umbraco.Core.BackOffice (source, context) => { var target = new UmbracoMembersIdentityUser(); - //target.DisableChangeTracking(); + target.DisableChangeTracking(); return target; }, (source, target, context) => { Map(source, target); - //target.ResetDirtyProperties(true); - //target.EnableChangeTracking(); + target.ResetDirtyProperties(true); + target.EnableChangeTracking(); }); } diff --git a/src/Umbraco.Core/Members/UmbracoMembersIdentityUser.cs b/src/Umbraco.Core/Members/UmbracoMembersIdentityUser.cs index fb92edd47d..fd4687c664 100644 --- a/src/Umbraco.Core/Members/UmbracoMembersIdentityUser.cs +++ b/src/Umbraco.Core/Members/UmbracoMembersIdentityUser.cs @@ -107,8 +107,7 @@ namespace Umbraco.Core.Members string username, string email, string memberTypeAlias, - string name, - string password = null) + string name) { if (string.IsNullOrWhiteSpace(username)) { diff --git a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs index 78d5d5554c..88605a7283 100644 --- a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs +++ b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using AutoFixture; @@ -64,6 +64,7 @@ namespace Umbraco.Tests.UnitTests.AutoFixture .Customize(new ConstructorCustomization(typeof(UsersController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(InstallController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(PreviewController), new GreedyConstructorQuery())) + .Customize(new ConstructorCustomization(typeof(MemberController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(BackOfficeController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(BackOfficeUserManager), new GreedyConstructorQuery())); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs new file mode 100644 index 0000000000..9924084bca --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoFixture.NUnit3; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Cache; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Events; +using Umbraco.Core.Mapping; +using Umbraco.Core.Members; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Security; +using Umbraco.Core.Serialization; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Infrastructure.Members; +using Umbraco.Tests.UnitTests.AutoFixture; +using Umbraco.Tests.UnitTests.Umbraco.Core.ShortStringHelper; +using Umbraco.Web; +using Umbraco.Web.BackOffice.Controllers; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Models; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers +{ + [TestFixture] + public class MemberControllerUnitTests + { + [Test] + [AutoMoqData] + public void PostSaveMember_WhenMemberIsNull_ExpectFailureResponse( + MemberController sut) + { + // arrange + // act + ArgumentNullException exception = Assert.ThrowsAsync(() => sut.PostSave(null)); + + // assert + //Assert.That(exception.Message, Is.EqualTo("Exception of type 'Umbraco.Web.Common.Exceptions.HttpResponse...")); + //Assert.That(exception.HResult, Is.EqualTo(42)); + } + + [Test] + [AutoMoqData] + public void PostSaveMember_WhenModelStateIsNotValid_ExpectFailureResponse( + MemberController sut) + { + // arrange + sut.ModelState.AddModelError("key", "Invalid model state"); + var fakeMemberData = new MemberSave() + { + Password = new ChangingPasswordModel() + { + Id = 123, + NewPassword = "i2ruf38vrba8^&T^", + OldPassword = null + } + }; + + // act + // assert + Assert.ThrowsAsync(() => sut.PostSave(fakeMemberData)); + } + + + [Test] + [AutoMoqData] + public async Task PostSaveMember_SaveNew_WhenAllIsSetupCorrectly_ExpectSuccessResponse( + [Frozen] IUmbracoMembersUserManager umbracoMembersUserManager, + IMemberTypeService memberTypeService, + IDataTypeService dataTypeService, + IMemberService memberService, + MapDefinitionCollection memberMapDefinition, + PropertyEditorCollection propertyEditorCollection, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IBackOfficeSecurity backOfficeSecurity) + { + // arrange + Member member = SetupMemberTestData(umbracoMembersUserManager, memberTypeService, memberMapDefinition, backOfficeSecurityAccessor, backOfficeSecurity, out UmbracoMapper mapper, out MemberSave fakeMemberData, out MemberDisplay memberDisplay, ContentSaveAction.SaveNew); + + Mock.Get(memberService).SetupSequence( + x => x.GetByEmail(It.IsAny())) + .Returns(() => null) + .Returns(() => member); + + MemberController sut = CreateSut(mapper, memberService, memberTypeService, umbracoMembersUserManager, dataTypeService, propertyEditorCollection, backOfficeSecurityAccessor); + + // act + ActionResult actualResult = await sut.PostSave(fakeMemberData); + + // assert + Assert.AreEqual(memberDisplay, actualResult.Value); + } + + [Test] + [AutoMoqData] + public async Task PostSaveMember_Save_WhenAllIsSetupCorrectly_ExpectSuccessResponse( + [Frozen] IUmbracoMembersUserManager umbracoMembersUserManager, + IMemberTypeService memberTypeService, + IDataTypeService dataTypeService, + IMemberService memberService, + MapDefinitionCollection memberMapDefinition, + PropertyEditorCollection propertyEditorCollection, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IBackOfficeSecurity backOfficeSecurity) + { + // arrange + Member member = SetupMemberTestData(umbracoMembersUserManager, memberTypeService, memberMapDefinition, backOfficeSecurityAccessor, backOfficeSecurity, out UmbracoMapper mapper, out MemberSave fakeMemberData, out MemberDisplay memberDisplay, ContentSaveAction.Save); + + Mock.Get(memberService).SetupSequence( + x => x.GetByEmail(It.IsAny())) + .Returns(() => null) + .Returns(() => member); + + MemberController sut = CreateSut(mapper, memberService, memberTypeService, umbracoMembersUserManager, dataTypeService, propertyEditorCollection, backOfficeSecurityAccessor); + + // act + ActionResult actualResult = await sut.PostSave(fakeMemberData); + + // assert + Assert.AreEqual(memberDisplay, actualResult.Value); + } + + [Test] + [AutoMoqData] + public async Task PostSaveMember_SaveNew_WhenMemberEmailAlreadyExists_ExpectSuccessResponse( + [Frozen] IUmbracoMembersUserManager umbracoMembersUserManager, + IMemberTypeService memberTypeService, + IDataTypeService dataTypeService, + IMemberService memberService, + MapDefinitionCollection memberMapDefinition, + PropertyEditorCollection propertyEditorCollection, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IBackOfficeSecurity backOfficeSecurity) + { + // arrange + Member member = SetupMemberTestData(umbracoMembersUserManager, memberTypeService, memberMapDefinition, backOfficeSecurityAccessor, backOfficeSecurity, out UmbracoMapper mapper, out MemberSave fakeMemberData, out MemberDisplay memberDisplay, ContentSaveAction.SaveNew); + + Mock.Get(memberService).SetupSequence( + x => x.GetByEmail(It.IsAny())) + .Returns(() => member); + + MemberController sut = CreateSut(mapper, memberService, memberTypeService, umbracoMembersUserManager, dataTypeService, propertyEditorCollection, backOfficeSecurityAccessor); + + // act + HttpResponseException exception = Assert.ThrowsAsync(() => sut.PostSave(fakeMemberData)); + + // assert + //Assert.That(exception.Message, Is.EqualTo("Exception of type 'Umbraco.Web.Common.Exceptions.HttpResponse...")); + //Assert.That(exception.Value, Is.EqualTo(42)); + } + + /// + /// Setup all standard member data for test + /// + private Member SetupMemberTestData(IUmbracoMembersUserManager umbracoMembersUserManager, + IMemberTypeService memberTypeService, + MapDefinitionCollection memberMapDefinition, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IBackOfficeSecurity backOfficeSecurity, + out UmbracoMapper mapper, + out MemberSave fakeMemberData, + out MemberDisplay memberDisplay, + ContentSaveAction contentAction) + { + Mock.Get(umbracoMembersUserManager) + .Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(() => IdentityResult.Success); + Mock.Get(memberTypeService).Setup(x => x.GetDefault()).Returns("fakeAlias"); + Mock.Get(backOfficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(backOfficeSecurity); + + var memberType = new MemberType(new DefaultShortStringHelper(new DefaultShortStringHelperConfig()), int.MinValue); + IMemberType testContentType = memberType; + + string fakePassword = "i2ruf38vrba8^&T^"; + var testName = "Test Name"; + var testEmail = "test@umbraco.com"; + var testUser = "TestUser"; + + var member = new Member(testName, testEmail, testUser, testContentType) {RawPasswordValue = fakePassword}; + mapper = new UmbracoMapper(memberMapDefinition); + + // TODO: reuse maps + mapper.Define((m, context) => new MemberDisplay() + { + Username = m.Username + }); + mapper.Define((m, context) => new Member(new Mock().Object)); + fakeMemberData = CreateFakeMemberData(member, contentAction); + + memberDisplay = new MemberDisplay() + { + }; + + return member; + } + + private MemberController CreateSut( + UmbracoMapper mapper, + IMemberService memberService, + IMemberTypeService memberTypeService, + IUmbracoMembersUserManager membersUserManager, + IDataTypeService dataTypeService, + PropertyEditorCollection propertyEditorCollection, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor) => + new MemberController( + new DefaultCultureDictionary( + new Mock().Object, + new HttpRequestAppCache(() => null)), + new LoggerFactory(), + new MockShortStringHelper(), + new DefaultEventMessagesFactory( + new Mock().Object), + new Mock().Object, + propertyEditorCollection, + mapper, + memberService, + memberTypeService, + membersUserManager, + dataTypeService, + backOfficeSecurityAccessor, + new ConfigurationEditorJsonSerializer()); + + private static MemberSave CreateFakeMemberData(IMember member, ContentSaveAction action) + { + var fakeMemberData = new MemberSave() + { + Password = new ChangingPasswordModel() + { + Id = 123, + NewPassword = member.RawPasswordValue, + OldPassword = null + }, + Name = member.Name, + Email = member.Email, + Username = member.Username, + PersistedContent = member, + PropertyCollectionDto = new ContentPropertyCollectionDto() + { + }, + Groups = new List(), + Alias = "fakeAlias", + ContentTypeAlias = "fakeContentType", + Action = action + }; + return fakeMemberData; + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index efaed903f2..ec0ea75986 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -192,7 +192,7 @@ namespace Umbraco.Web.BackOffice.Controllers [OutgoingEditorModelEvent] public MemberDisplay GetByKey(Guid key) { - // TODO: this is not finding the key currently + // TODO: this is not finding the key currently after member creation IMember foundMember = _memberService.GetByKey(key); if (foundMember == null) { @@ -441,12 +441,6 @@ namespace Umbraco.Web.BackOffice.Controllers $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password"); } } - else - { - ModelState.AddPropertyError( - new ValidationResult("Password cannot be empty", new[] { "value" }), - $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password"); - } } private string MapErrors(List result) @@ -512,7 +506,7 @@ namespace Umbraco.Web.BackOffice.Controllers // no password changes then exit ? if (memberSave.Password != null) { - // set the password + // TODO: set the password memberSave.PersistedContent.RawPasswordValue = _memberManager.GeneratePassword(); } } diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs index 4ebd8f7cc5..dd82d73d62 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; @@ -63,6 +63,7 @@ namespace Umbraco.Web.BackOffice.Trees /// public ActionResult GetTreeNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) { + //TODO: this is currently throwing an exception var node = GetSingleTreeNode(id, queryStrings); //add the tree alias to the node since it is standalone (has no root for which this normally belongs)