diff --git a/Bonobo.Git.Server.Test/Bonobo.Git.Server.Test.csproj b/Bonobo.Git.Server.Test/Bonobo.Git.Server.Test.csproj index 2e69aebe..51d8e23f 100644 --- a/Bonobo.Git.Server.Test/Bonobo.Git.Server.Test.csproj +++ b/Bonobo.Git.Server.Test/Bonobo.Git.Server.Test.csproj @@ -50,7 +50,7 @@ - ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + ..\packages\Castle.Core.4.4.1\lib\net45\Castle.Core.dll ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll @@ -69,6 +69,7 @@ ..\packages\Moq.4.15.2\lib\net45\Moq.dll + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True @@ -170,13 +171,20 @@ - - + + + + + + + + + diff --git a/Bonobo.Git.Server.Test/Unit/ControllerDependendencyBuilders.cs b/Bonobo.Git.Server.Test/Unit/ControllerDependendencyBuilders.cs new file mode 100644 index 00000000..c20a38c0 --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerDependendencyBuilders.cs @@ -0,0 +1,73 @@ +using Bonobo.Git.Server.Data; +using Bonobo.Git.Server.Models; +using Bonobo.Git.Server.Security; +using Moq; +using System; +using System.Collections.Generic; + +namespace Bonobo.Git.Server.Test.Unit +{ + public static class ControllerDependendencyBuilders + { + // TeamRepository Mock + public static Mock SetupToSucceedWhenCreatingATeam(this Mock teamRepositoryMock) + { + teamRepositoryMock.Setup(r => r.Create(It.IsAny())) + .Returns(true); + return teamRepositoryMock; + } + + public static Mock SetupToReturnASpecificTeamWhenCallingGetTeamMethod(this Mock teamRepositoryMock, Guid requestedGuid) + { + teamRepositoryMock.Setup(t => t.GetTeam(requestedGuid)) + .Returns(new TeamModel + { + Id = requestedGuid, + Members = new UserModel[0] + }); + return teamRepositoryMock; + } + + // MembershipService Mock + public static Mock SetupToReturnAnEmptyUserModelListWhenCallingGetAllUsers(this Mock membershipServiceMock) + { + membershipServiceMock.Setup(m => m.GetAllUsers()) + .Returns(new List()); + return membershipServiceMock; + } + + public static Mock SetupToReturnARequestedUserModel(this Mock membershipServiceMock, string requestedUserName) + { + membershipServiceMock.Setup(m => m.GetUserModel(requestedUserName)) + .Returns(new UserModel { Username = requestedUserName }); + return membershipServiceMock; + } + + public static Mock SetupToGenerateResetToken(this Mock membershipServiceMock, string token, string requestedUserName) + { + membershipServiceMock.Setup(m => m.GenerateResetToken(requestedUserName)) + .Returns(token); + return membershipServiceMock; + } + + // IRepositoryRepository Mock + public static Mock SetupToReturnAnEmptyListForASpecificIdWhenCallingGetTeamRepositories(this Mock repositoryRepositoryMock, Guid requestedId) + { + repositoryRepositoryMock.Setup(r => r.GetTeamRepositories(new[] { requestedId })) + .Returns(new List()); + return repositoryRepositoryMock; + } + + public static Mock SetupToReturnAModelWithASpecificIdWhenCallingGetRepositoryMethod(this Mock repositoryRepositoryMock, Guid guid) + { + repositoryRepositoryMock.Setup(s => s.GetRepository(guid)) + .Returns(new RepositoryModel + { + Id = guid, + Administrators = new UserModel[0], + Name = "name" + }); + return repositoryRepositoryMock; + } + } +} diff --git a/Bonobo.Git.Server.Test/Unit/ControllerTests.AccountControllerTests.cs b/Bonobo.Git.Server.Test/Unit/ControllerTests.AccountControllerTests.cs new file mode 100644 index 00000000..3d706788 --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerTests.AccountControllerTests.cs @@ -0,0 +1,363 @@ +using Bonobo.Git.Server.App_GlobalResources; +using Bonobo.Git.Server.Configuration; +using Bonobo.Git.Server.Controllers; +using Bonobo.Git.Server.Models; +using Bonobo.Git.Server.Security; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Configuration; +using System.Security.Principal; +using System.Web.Mvc; + +namespace Bonobo.Git.Server.Test.Unit +{ + public partial class ControllerTests + { + [TestClass] + public class AccountControllerTests : ControllerTests + { + [TestInitialize] + public void TestInitialize() + { + sut = new AccountController(); + } + + // get Delete + // post Delete + + [TestMethod] + public void Get_Edit_Executed_With_Null_Parameters__Throws_NullReferenceException() + { + try + { + // arrange + SetHttpContextMockIntoSUT(Guid.Empty); + // act + var result = SutAs().Edit(null); + } + catch (NullReferenceException) + { + return; + } + //assert + Assert.Fail(); + } + + [TestMethod] + public void Get_Edit_When_User_Is_Unknown_And_Resulting_Model_Is_Null__Stays_On_View() + { + // Arrange + Guid userid = Guid.NewGuid(); + SetHttpContextMockIntoSUT(userid); + SetupMembershipServiceMockIntoSUT(); + BindModelToController(userid); + + // act + var result = SutAs().Edit(userid); + + // assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + var viewResult = result as ViewResult; + Assert.IsNotNull(viewResult); + Assert.IsNull(viewResult.Model); + } + + [TestMethod] + public void Get_Edit_When_Non_Admin_User_Queries_Other_UserId__Gets_Redirected_To_Home_Unauthorized() + { + // Arrange + Guid userid = Guid.NewGuid(); + SetHttpContextMockIntoSUT(userid); + BindModelToController(userid); + + // act + var result = SutAs().Edit(Guid.NewGuid()); + + // assert + AssertRedirectToHomeUnauthorized(result); + } + + [TestMethod] + public void Get_Edit_When_Admin_User_Queries_Other_User_Id_And_Resulting_Model_Is_Null__Stays_On_View() + { + // Arrange + Guid userid = Guid.NewGuid(); + Guid otherId = Guid.NewGuid(); + SetHttpContextMockIntoSUT(userid); + SetupMembershipServiceMockIntoSUT(); + SetupUserAsAdmin(); + BindModelToController(userid); + + // act + var result = SutAs().Edit(otherId); + var viewResult = result as ViewResult; + + // assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + Assert.IsNotNull(viewResult); + Assert.IsNull(viewResult.Model); + } + + [TestMethod] + public void Get_Edit_When_User_Is_Known_And_Resulting_Model_Is_Not_Null__Stays_On_View() + { + // Arrange + Guid userId = Guid.NewGuid(); + SetHttpContextMockIntoSUT(userId); + SetupMembershipServiceMockIntoSUT(); + BindModelToController(userId); + membershipServiceMock.Setup(m => m.GetUserModel(userId)) + .Returns(new UserModel + { + Id = userId, + Username = "Username", + GivenName = "Given", + Surname = "Sur", + Email = "email" + }); + + string[] allRoles = new string[] { "role1", "role2" }; + string[] selectedRoles = new string[] { "role1" }; + SetupRolesProviderMockIntoSUT(); + roleProviderMock.Setup(r => r.GetAllRoles()) + .Returns(allRoles); + roleProviderMock.Setup(r => r.GetRolesForUser(userId)) + .Returns(selectedRoles); + + // act + var result = SutAs().Edit(userId); + var viewResult = result as ViewResult; + var userEditModel = viewResult?.Model as UserEditModel; + + // assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + Assert.IsNotNull(viewResult); + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(UserEditModel)); + Assert.AreEqual(userEditModel.Id, userId); + Assert.AreEqual(userEditModel.Username, "Username"); + Assert.AreEqual(userEditModel.Name, "Given"); + Assert.AreEqual(userEditModel.Surname, "Sur"); + Assert.AreEqual(userEditModel.Email, "email"); + Assert.AreEqual(userEditModel.Roles, allRoles); + Assert.AreEqual(userEditModel.SelectedRoles, selectedRoles); + } + + [TestMethod] + public void Post_Edit_With_Unbound_Empty_Model_And_Environment_Unprepared__Throws_A_NullReferenceException() + { + try + { + SutAs().Edit(new UserEditModel()); + } + catch (NullReferenceException) + { + return; + } + Assert.Fail(); + } + + [TestMethod] + public void Post_Edit_With_Unbound_Bare_Model_Data_Referring_To_The_Same_User__Returns_Weird_Data() + { + // Arrange + Guid userId = Guid.NewGuid(); + UserEditModel model = new UserEditModel { Id = userId }; + + SetupMinimalEnvironment(userId); + + // act + var result = SutAs().Edit(model); + + AssertMinimalViewResult(result, userId); + AssertSuccessfulResponse(result); + } + + [TestMethod] + public void Post_Edit_With_Unbound_Bare_Model_And_Admin_And_DemoActive_Configuration_Set_And_Data_Referring_To_The_Same_UserId__Redirects_To_Home_Unauthorized() + { + // Arrange + ConfigurationManager.AppSettings["demoModeActive"] = "true"; + // AuthenticationSettings class controller needs to run again because we changed the demoModelActive appSetting + ReinitializeStaticClass(typeof(AuthenticationSettings)); + + Guid userId = Guid.NewGuid(); + SetupMinimalEnvironment(userId); + UserEditModel model = new UserEditModel { Id = userId }; + SetupUserAsAdmin(); + + // Act + var result = SutAs().Edit(model); + + // Assert + AssertRedirectToHomeUnauthorized(result); + } + + [TestMethod] + public void Post_Edit_With_Bound_Empty_Model_And_Correct_Environment__Passes_Execution() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + SetupMembershipServiceMockIntoSUT(); + SetupRolesProviderMockIntoSUT(); + UserEditModel model = new UserEditModel(); + BindModelToController(model); + + // Act + var result = SutAs().Edit(model); + + // Assert + AssertMinimalViewResult(result, Guid.Empty); + } + + [TestMethod] + public void Post_Edit_With_Bound_Bare_Model_Data_Referring_To_The_Same_User__Returns_Null_Update_Success_In_ViewBag() + { + // Arrange + Guid userId = Guid.NewGuid(); + SetupMinimalEnvironment(userId); + UserEditModel model = new UserEditModel { Id = userId }; + BindModelToController(model); + + // act + var result = SutAs().Edit(model); + + AssertMinimalViewResult(result, userId); + Assert.IsFalse(sut.ModelState.IsValid); + Assert.AreEqual(4, sut.ModelState.Count); + Assert.IsNull((result as ViewResult).ViewBag.UpdateSuccess); + } + + [TestMethod] + public void Post_Edit_With_Bound_Bare_Model_Data_Referring_To_Another_User__Redirects_To_Home_Unauthorized() + { + // Arrange + Guid userId = Guid.NewGuid(); + Guid otherId = Guid.NewGuid(); + SetupMinimalEnvironment(userId); + UserEditModel model = new UserEditModel { Id = otherId }; + BindModelToController(model); + + // act + var result = SutAs().Edit(model); + + // assert + Assert.IsNotNull(result); + + AssertRedirectToHomeUnauthorized(result); + } + + [TestMethod] + public void Post_Edit_With_Bound_Invalid_Model_With_NewPassword_Not_Empty_And_OldPassword_Empty__Returns_Expected_ModelState() + { + // Arrange + Guid userId = Guid.NewGuid(); + SetupMinimalEnvironment(userId); + UserEditModel model = new UserEditModel + { + Id = userId, + Username = "Username", + Name = "Name", + Surname = "Surname", + Email = "email@test.com", + NewPassword = "NewPassword" + }; + BindModelToController(model); + + // Act + var result = SutAs().Edit(model); + + // Assert + AssertMinimalViewResult(result, userId); + Assert.IsFalse(sut.ModelState.IsValid); + + string expectedMessageForConfirmPassword = String.Format(Resources.Validation_Compare, "Confirm Password", "New Password"); + string actualMessageForConfirmPassword = sut.ModelState["ConfirmPassword"].Errors[0].ErrorMessage; + + Assert.AreEqual(expectedMessageForConfirmPassword, actualMessageForConfirmPassword); + } + + [TestMethod] + public void Post_Edit_With_Bound_Valid_Model_Data_Referring_To_The_Same_User__Returns_Data_From_The_User() + { + // Arrange + Guid userId = Guid.NewGuid(); + SetupMinimalEnvironment(userId); + + UserEditModel model = new UserEditModel + { + Id = userId, + Username = "Username", + Name = "Name", + Surname = "Surname", + Email = "email@test.com", + }; + BindModelToController(model); + + // act + var result = SutAs().Edit(model); + + AssertMinimalViewResult(result, userId); + AssertSuccessfulResponse(result); + } + + // get Create + // post Create + + private void SetupMinimalEnvironment(Guid userId) + { + SetHttpContextMockIntoSUT(userId); + SetupMembershipServiceMockIntoSUT(); + SetupRolesProviderMockIntoSUT(); + } + + private static void AssertMinimalViewResult(ActionResult result, Guid userId) + { + // assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + + var viewResult = result as ViewResult; + + Assert.IsNotNull(viewResult); + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(UserEditModel)); + + var userEditModel = viewResult.Model as UserEditModel; + + Assert.IsNotNull(userEditModel); + Assert.AreEqual(userId, userEditModel.Id); + Assert.IsNotNull(userEditModel.Roles); + Assert.IsNotNull(viewResult.ViewBag); + } + + private void AssertSuccessfulResponse(ActionResult result) + { + Assert.IsTrue(sut.ModelState.IsValid); + + ViewResult viewResult = result as ViewResult; + + Assert.IsTrue(viewResult.ViewBag.UpdateSuccess); + } + + private void SetupMembershipServiceMockIntoSUT() + { + membershipServiceMock = new Mock(); + SutAs().MembershipService = membershipServiceMock.Object; + } + + private void SetupRolesProviderMockIntoSUT() + { + roleProviderMock = new Mock(); + SutAs().RoleProvider = roleProviderMock.Object; + } + + private Mock membershipServiceMock; + private Mock roleProviderMock; + } + } +} \ No newline at end of file diff --git a/Bonobo.Git.Server.Test/Unit/ControllerTests.HomeControllerTests.cs b/Bonobo.Git.Server.Test/Unit/ControllerTests.HomeControllerTests.cs new file mode 100644 index 00000000..4f631793 --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerTests.HomeControllerTests.cs @@ -0,0 +1,200 @@ +using Bonobo.Git.Server.App_GlobalResources; +using Bonobo.Git.Server.Controllers; +using Bonobo.Git.Server.Models; +using Bonobo.Git.Server.Security; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Security.Principal; +using System.Web; +using System.Web.Mvc; + +namespace Bonobo.Git.Server.Test.Unit +{ + public partial class ControllerTests + { + [TestClass] + public class HomeControllerTests : ControllerTests + { + [TestInitialize] + public void TestInitialize() + { + sut = new HomeController(); + } + + [TestMethod] + public void Get_LogOn_Called_With_Null_Url__Throws_No_Exceptions() + { + // Arrange + string returnUrl = null; + + // Act + var result = SutAs().LogOn(returnUrl); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + + var viewResult = result as ViewResult; + + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(LogOnModel)); + + var logOnModel = viewResult.Model as LogOnModel; + Assert.IsNull(logOnModel.ReturnUrl); + } + + [TestMethod] + public void Get_LogOn_Called_With_Non_Null_Url__Returns_ReturnUrl_In_Model() + { + // Arrange + const string returnUrl = "theurl"; + + // Act + var result = SutAs().LogOn(returnUrl); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + + var viewResult = result as ViewResult; + + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(LogOnModel)); + + var logOnModel = viewResult.Model as LogOnModel; + Assert.AreEqual(returnUrl, logOnModel.ReturnUrl); + } + + [TestMethod] + public void Post_Logon_Called_With_Null_Model__Throws_NullReferenceException() + { + // Arrange + LogOnModel model = null; + + try + { + // Act + SutAs().LogOn(model); + } + catch (NullReferenceException) + { + return; + } + + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Post_Logon_Called_With_Empty_Model_And_Controller_Environment__Returns_ViewResult() + { + // Arrange + ArrangeBareContext(); + + membershipServiceMock.Setup(mp => mp.ValidateUser(It.IsAny(), It.IsAny())) + .Returns(ValidationResult.Failure); + LogOnModel model = new LogOnModel(); + + // Act + var result = SutAs().LogOn(model); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + } + + [TestMethod] + public void Post_Logon_Called_With_Bare_Model_Request_And_Good_Credentials__Returns_EmptyResult() + { + // Arrange + ArrangeBareContext(); + + const string username = "username"; + const string password = "password"; + membershipServiceMock.Setup(mp => mp.ValidateUser(username, password)) + .Returns(ValidationResult.Success); + httpContextMock.SetupGet(hc => hc.Request) + .Returns(new Mock().Object); + LogOnModel model = new LogOnModel { Username = username, Password = password }; + + // Act + var result = SutAs().LogOn(model); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(EmptyResult)); + } + + [TestMethod] + public void Post_Logon_Called_With_Bare_Model_And_No_Authorization__Returns_RedirectResult() + { + // Arrange + ArrangeBareContext(); + + const string username = "username"; + const string password = "password"; + membershipServiceMock.Setup(mp => mp.ValidateUser(username, password)) + .Returns(ValidationResult.NotAuthorized); + httpContextMock.SetupGet(hc => hc.Request) + .Returns(new Mock().Object); + LogOnModel model = new LogOnModel { Username = username, Password = password }; + + // Act + var result = SutAs().LogOn(model); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(RedirectResult)); + + var redirectResult = result as RedirectResult; + Assert.AreEqual("~/Home/Unauthorized", redirectResult.Url); + } + + [TestMethod] + public void Post_Logon_Called_With_Bare_Model_And_Bad_Credentials__Returns_ViewResult() + { + // Arrange + ArrangeBareContext(); + + const string username = "username"; + const string password = "password"; + membershipServiceMock.Setup(mp => mp.ValidateUser(username, password)) + .Returns(ValidationResult.Failure); + httpContextMock.SetupGet(hc => hc.Request) + .Returns(new Mock().Object); + LogOnModel model = new LogOnModel { Username = username, Password = password }; + + // Act + var result = SutAs().LogOn(model); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + Assert.IsFalse(sut.ModelState.IsValid); + Assert.AreEqual(Resources.Home_LogOn_UsernamePasswordIncorrect, sut.ModelState[""].Errors[0].ErrorMessage); + } + + // get ResetPassword + // post ResetPassword + // get ForgotPassword + // post ForgotPassword + + private void ArrangeBareContext() + { + sut.ControllerContext = CreateControllerContext(); + SetupMembershipServiceMockIntoSUT(); + SutAs().AuthenticationProvider = new Mock().Object; + SutAs().Url = new Mock().Object; + } + + private void SetupMembershipServiceMockIntoSUT() + { + membershipServiceMock = new Mock(); + SutAs().MembershipService = membershipServiceMock.Object; + } + + private Mock membershipServiceMock; + } + } +} diff --git a/Bonobo.Git.Server.Test/Unit/ControllerTests.RepositoryControllerTests.cs b/Bonobo.Git.Server.Test/Unit/ControllerTests.RepositoryControllerTests.cs new file mode 100644 index 00000000..73c15286 --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerTests.RepositoryControllerTests.cs @@ -0,0 +1,73 @@ +using Bonobo.Git.Server.Controllers; +using Bonobo.Git.Server.Data; +using Bonobo.Git.Server.Models; +using Bonobo.Git.Server.Security; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; + +namespace Bonobo.Git.Server.Test.Unit +{ + public partial class ControllerTests + { + [TestClass] + public class RepositoryControllerTests : ControllerTests + { + [TestInitialize] + public void TestInitialize() + { + sut = new RepositoryController(); + } + + // get Edit + [TestMethod] + public void Get_Edit_Executed_With_Empty_Id__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Edit(Guid.Empty); + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + // post Edit + [TestMethod] + public void Post_Edit_Executed_With_Random_id_returns_xx() + { + // arrange + ArrangeUserConfiguration(); + var guid = Guid.NewGuid(); + + var repositoryController = SutAs(); + var repositoryRepositoryMock = SetupMock().SetupToReturnAModelWithASpecificIdWhenCallingGetRepositoryMethod(guid); + var membershipServiceMock = SetupMock().SetupToReturnAnEmptyUserModelListWhenCallingGetAllUsers(); + var teamRepositoryMock = SetupMock(); + teamRepositoryMock.Setup(s => s.GetAllTeams()) + .Returns(new List { }); + repositoryController.RepositoryRepository = repositoryRepositoryMock.Object; + repositoryController.MembershipService = membershipServiceMock.Object; + repositoryController.TeamRepository = teamRepositoryMock.Object; + + //act + var result = repositoryController.Edit(guid); + + Assert.IsNotNull(result); + } + + // get Create + // post Create + // get Delete + // post Delete + // get Clone + // post Clone + } + } +} diff --git a/Bonobo.Git.Server.Test/Unit/ControllerTests.SettingsControllerTests.cs b/Bonobo.Git.Server.Test/Unit/ControllerTests.SettingsControllerTests.cs new file mode 100644 index 00000000..32241734 --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerTests.SettingsControllerTests.cs @@ -0,0 +1,141 @@ +using Bonobo.Git.Server.Configuration; +using Bonobo.Git.Server.Controllers; +using Bonobo.Git.Server.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Configuration; +using System.Web; +using System.Web.Mvc; + +namespace Bonobo.Git.Server.Test.Unit +{ + public partial class ControllerTests + { + [TestClass] + public class SettingsControllerTests : ControllerTests + { + [TestInitialize] + public void TestInitialize() + { + sut = new SettingsController(); + ConfigurationManager.AppSettings["demoModeActive"] = "false"; + // AuthenticationSettings class controller needs to run again because we changed the demoModelActive appSetting + ReinitializeStaticClass(typeof(AuthenticationSettings)); + } + + [TestMethod] + public void Get_Index_Called_With_Uninitialized_Configuration__Throws_TypeInitializationException_Or_DirectoryNotFoundException() + { + // Arrange + ArrangeUserConfiguration(); + + // Act + var result = SutAs().Index(); + + // Assert + Assert.IsNotNull(result); + } + + [TestMethod] + public void Get_Index_Called_With_Initialized_Configuration__Throws_No_Exceptions() + { + // Arrange + ArrangeUserConfiguration(); + + // Act + var result = SutAs().Index(); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + + var viewResult = result as ViewResult; + + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(GlobalSettingsModel)); + } + + [TestMethod] + public void Post_Index_Called_With_Null_Model__Throws_NullReferenceException() + { + // Arrange + ArrangeUserConfiguration(); + + try + { + // Act + SutAs().Index(null); + } + catch (NullReferenceException) + { + return; + } + + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Post_Index_Called_With_Unbound_Model__Throws_NullReferenceException() + { + // Arrange + ArrangeUserConfiguration(); + + try + { + // Act + SutAs().Index(new GlobalSettingsModel()); + + } + catch (NullReferenceException) + { + return; + } + + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Post_Index_Called_With_Invalid_Model_And_ControllerContext_Set__Returns_ViewResult() + { + // Arrange + ArrangeUserConfiguration(); + sut.ControllerContext = CreateControllerContext(); + httpContextMock.SetupGet(hc => hc.Server) + .Returns(new Mock().Object); + GlobalSettingsModel model = new GlobalSettingsModel(); + BindModelToController(model); + + // Act + var result = SutAs().Index(model); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + } + + [TestMethod] + public void Post_Index_Called_With_Valid_Model_With_Non_Existent_Folder_And_ControllerContext_Set__Returns_ViewResult_With_Errors() + { + // Arrange + ArrangeUserConfiguration(); + sut.ControllerContext = CreateControllerContext(); + httpContextMock.SetupGet(hc => hc.Server) + .Returns(new Mock().Object); + GlobalSettingsModel model = new GlobalSettingsModel { RepositoryPath = "-" }; + BindModelToController(model); + + // Act + var result = SutAs().Index(model); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + Assert.IsFalse(sut.ModelState.IsValid); + Assert.AreEqual(1, sut.ModelState.Count); + } + } + } +} diff --git a/Bonobo.Git.Server.Test/Unit/ControllerTests.TeamControllerTests.cs b/Bonobo.Git.Server.Test/Unit/ControllerTests.TeamControllerTests.cs new file mode 100644 index 00000000..2c9a1814 --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerTests.TeamControllerTests.cs @@ -0,0 +1,458 @@ +using Bonobo.Git.Server.Controllers; +using Bonobo.Git.Server.Data; +using Bonobo.Git.Server.Models; +using Bonobo.Git.Server.Security; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; + +namespace Bonobo.Git.Server.Test.Unit +{ + public partial class ControllerTests + { + [TestClass] + public class TeamControllerTests : ControllerTests + { + [TestInitialize] + public void TestInitialize() + { + sut = new TeamController(); + } + + // get Edit + [TestMethod] + public void Get_Edit_Executed_Without_ControllerContext_Setup__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Edit(Guid.Empty); + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Get_Edit_Executed_With_ControllerContext_Setup__Returns_ViewResult_With_Null_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var teamRepositoryMock = SetupMock(); + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + + // Act + var result = teamController.Edit(Guid.Empty); + + // Assert + Assert.IsTrue(sut.ModelState.IsValid); + var viewResult = AssertAndGetViewResult(result); + + Assert.IsNull(viewResult.Model); + } + + [TestMethod] + public void Get_Edit_Executed_With_ControllerContext_Setup__Returns_ViewResult_With_Not_Null_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var requestedGuid = Guid.NewGuid(); + var teamRepositoryMock = SetupMock().SetupToReturnASpecificTeamWhenCallingGetTeamMethod(requestedGuid); + var membershipServiceMock = SetupMock().SetupToReturnAnEmptyUserModelListWhenCallingGetAllUsers(); + + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + teamController.MembershipService = membershipServiceMock.Object; + + // Act + var result = teamController.Edit(requestedGuid); + + // Assert + Assert.IsTrue(sut.ModelState.IsValid); + var viewResult = AssertAndGetViewResult(result); + + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(TeamEditModel)); + + var teamEditModel = viewResult.Model as TeamEditModel; + Assert.AreEqual(requestedGuid, teamEditModel.Id); + } + + // post Edit + [TestMethod] + public void Post_Edit_Executed_With_Null_Model_And_Without_ControllerContext_Setup__Throws_NullReferenceException() + { + try + { + SutAs().Edit(null); + } + catch (NullReferenceException) + { + return; + } + Assert.Fail(); + } + + [TestMethod] + public void Post_Edit_Executed_With_Bare_Model_And_Without_ControllerContext_Setup__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Edit(new TeamEditModel()); + + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Post_Edit_Executed_With_Null_Model_And_With_ControllerContext_Setup__Throws_NullReferenceException() + { + sut.ControllerContext = CreateControllerContext(); + + try + { + SutAs().Edit(null); + } + catch (NullReferenceException) + { + return; + } + Assert.Fail(); + } + + [TestMethod] + public void Post_Edit_Executed_With_NonExistent_Model_And_With_ControllerContext_Setup__Returns_ViewResult() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var teamRepositoryMock = SetupMock(); + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + + var model = new TeamEditModel(); + BindModelToController(model); + + // Act + var result = teamController.Edit(model); + + // Assert + var viewResult = AssertAndGetViewResult(result); + Assert.IsNull(viewResult.Model); + } + + [TestMethod] + public void Post_Edit_Executed_With_Existent_Model_And_With_ControllerContext_Setup__Returns_ViewResult_With_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var requestedGuid = Guid.NewGuid(); + var membershipServiceMock = SetupMock().SetupToReturnAnEmptyUserModelListWhenCallingGetAllUsers(); + var teamRepositoryMock = SetupMock().SetupToReturnASpecificTeamWhenCallingGetTeamMethod(requestedGuid); + + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + teamController.MembershipService = membershipServiceMock.Object; + + var model = new TeamEditModel { Id = requestedGuid }; + + // Act + var result = teamController.Edit(model); + + // Assert + var viewResult = AssertAndGetViewResult(result); + Assert.IsNotNull(viewResult.Model); + } + + // get Create + [TestMethod] + public void Get_Create_Executed_Without_Arranging_TeamRepository__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Create(); + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Get_Create_Executed_Arranging_MembershipService__Returns_ViewResult() + { + // Arrange + var membershipServiceMock = SetupMock().SetupToReturnAnEmptyUserModelListWhenCallingGetAllUsers(); + var teamController = SutAs(); + teamController.MembershipService = membershipServiceMock.Object; + + // Act + var result = teamController.Create(); + + // Assert + var viewResult = AssertAndGetViewResult(result); + Assert.IsNotNull(viewResult.Model); + Assert.IsInstanceOfType(viewResult.Model, typeof(TeamEditModel)); + } + + // post Create + [TestMethod] + public void Post_Create_Executed_With_Null_ViewModel__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Create(null); + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Post_Create_Executed_With_NonNull_ViewModel_Without_Arranging_TeamRepository__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Create(new TeamEditModel()); + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Post_Create_Executed_With_Invalid_ViewModel__Returns_ViewResult_And_Invalid_ModelState() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var teamRepositoryMock = SetupMock(); + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + + var model = new TeamEditModel(); + BindModelToController(model); + + // Act + var result = teamController.Create(model); + + // Assert + Assert.IsFalse(teamController.ModelState.IsValid); + var viewResult = AssertAndGetViewResult(result); + Assert.IsNotNull(viewResult.Model); + } + + [TestMethod] + public void Post_Create_Executed_With_Valid_ViewModel_Arranging_TeamRepository__Returns_RedirectToViewResult_With_TempData_Flag_True_And_ModelId() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var teamRepositoryMock = SetupMock().SetupToSucceedWhenCreatingATeam(); + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + + var model = new TeamEditModel + { + Name = "name", + Id = Guid.NewGuid() + }; + + // Act + var result = teamController.Create(model); + + // Assert + Assert.AreEqual(true, teamController.TempData["CreateSuccess"]); + var redirectToRouteResult = AssertAndGetRedirectToRouteResult(result); + Assert.AreEqual(1, redirectToRouteResult.RouteValues.Count); + Assert.AreEqual("action", redirectToRouteResult.RouteValues.Keys.First()); + Assert.AreEqual("Index", redirectToRouteResult.RouteValues["action"]); + Assert.AreEqual(2, sut.TempData.Count); + Assert.AreEqual(true, sut.TempData["CreateSuccess"]); + Assert.AreEqual(model.Id, sut.TempData["NewTeamId"]); + } + + // get Delete + [TestMethod] + public void Get_Delete_Executed_Without_Arranging_TeamRepository_With_Empty_Id__Throws_NullReferenceException() + { + // Arrange + try + { + // Act + SutAs().Delete(Guid.Empty); + } + catch (NullReferenceException) + { + return; + } + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Get_Delete_Executed_Arranging_TeamRepository_With_No_Setup_For_GetTeam_With_Empty_Id__Returns_Null_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var teamRepositoryMock = SetupMock(); + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + + // Act + var result = teamController.Delete(Guid.Empty); + + // Assert + var viewResult = AssertAndGetViewResult(result); + + Assert.IsNull(viewResult.Model); + } + + [TestMethod] + public void Get_Delete_Executed_Arranging_TeamRepository_With_Setting_Up_GetTeam_With_Valid_Id__Returns_NonNull_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var requestedId = Guid.NewGuid(); + var teamRepositoryMock = SetupMock().SetupToReturnASpecificTeamWhenCallingGetTeamMethod(requestedId); + var membershipServiceMock = SetupMock().SetupToReturnAnEmptyUserModelListWhenCallingGetAllUsers(); + + var teamController = SutAs(); + teamController.TeamRepository = teamRepositoryMock.Object; + teamController.MembershipService = membershipServiceMock.Object; + + // Act + var result = teamController.Delete(requestedId); + + // Assert + var viewResult = AssertAndGetViewResult(result); + Assert.IsNotNull(viewResult.Model); + } + + // post Delete + [TestMethod] + public void Post_Delete_Executed_Without_Arranging_TeamRepository_With_Null_Model__Returns_RedirectToActionResult_Without_TempData() + { + // Arrange + + // Act + var result = SutAs().Delete(null); + + // Assert + + var redirectToRouteResult = AssertAndGetRedirectToRouteResult(result); + Assert.AreEqual(1, redirectToRouteResult.RouteValues.Count); + Assert.AreEqual("Index", redirectToRouteResult.RouteValues["action"]); + + Assert.AreEqual(0, sut.TempData.Count); + } + + [TestMethod] + public void Post_Delete_Executed_Arranging_TeamRepository_With_Valid_Model__Returns_RedirectToActionResult_With_TempData() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var requestedId = Guid.NewGuid(); + var teamController = SutAs(); + var teamRepositoryMock = SetupMock().SetupToReturnASpecificTeamWhenCallingGetTeamMethod(requestedId); + teamController.TeamRepository = teamRepositoryMock.Object; + + // Act + var result = teamController.Delete(new TeamEditModel { Id = requestedId }); + + // Assert + var redirectToRouteResult = AssertAndGetRedirectToRouteResult(result); + + Assert.AreEqual(1, redirectToRouteResult.RouteValues.Count); + Assert.AreEqual("Index", redirectToRouteResult.RouteValues["action"]); + + Assert.AreEqual(1, sut.TempData.Count); + Assert.AreEqual("DeleteSuccess", sut.TempData.Keys.FirstOrDefault()); + Assert.AreEqual(true, sut.TempData["DeleteSuccess"]); + } + + // get Detail + [TestMethod] + public void Get_Detail_Without_Arranging_TeamRepository__Throws_NullReferenceException() + { + // Arrange + + // Act + try + { + SutAs().Detail(Guid.Empty); + } + catch (NullReferenceException) + { + return; + } + + // Assert + Assert.Fail(); + } + + [TestMethod] + public void Get_Detail_Arranging_TeamRepository_With_Unknown_Id__Returns_ViewResult_With_Null_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var teamController = SutAs(); + var teamRepositoryMock = SetupMock(); + teamController.TeamRepository = teamRepositoryMock.Object; + + // Act + var result = teamController.Detail(Guid.Empty); + + // Assert + var viewResult = AssertAndGetViewResult(result); + Assert.IsNull(viewResult.Model); + } + + [TestMethod] + public void Get_Detail_Arranging_TeamRepository_With_Known_Id__Returns_ViewResult_With_Null_Model() + { + // Arrange + sut.ControllerContext = CreateControllerContext(); + var requestedId = Guid.NewGuid(); + var teamController = SutAs(); + var teamRepositoryMock = SetupMock().SetupToReturnASpecificTeamWhenCallingGetTeamMethod(requestedId); + var membershipServiceMock = SetupMock(); + var repositoryRepositoryMock = SetupMock().SetupToReturnAnEmptyListForASpecificIdWhenCallingGetTeamRepositories(requestedId); + + teamController.TeamRepository = teamRepositoryMock.Object; + teamController.MembershipService = membershipServiceMock.Object; + teamController.RepositoryRepository = repositoryRepositoryMock.Object; + + // Act + var result = teamController.Detail(requestedId); + + // Assert + var viewResult = AssertAndGetViewResult(result); + Assert.IsNotNull(viewResult.Model); + } + } + } +} diff --git a/Bonobo.Git.Server.Test/Unit/ControllerTests.cs b/Bonobo.Git.Server.Test/Unit/ControllerTests.cs new file mode 100644 index 00000000..4b0b2c9d --- /dev/null +++ b/Bonobo.Git.Server.Test/Unit/ControllerTests.cs @@ -0,0 +1,141 @@ +using Bonobo.Git.Server.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Security.Claims; +using System.Security.Principal; +using System.Web; +using System.Web.Mvc; + +namespace Bonobo.Git.Server.Test.Unit +{ + public partial class ControllerTests + { + private T SutAs() where T : Controller => sut as T; + + private Mock SetupMock() where T : class + { + return new Mock(); + } + + private void SetupUserAsAdmin() + { + claimsPrincipalMock.Setup(p => p.IsInRole(Definitions.Roles.Administrator)) + .Returns(true); + } + + + private void SetHttpContextMockIntoSUT(Guid id) + { + var claimsIdentity = new ClaimsIdentity(new List { new Claim(ClaimTypes.NameIdentifier, id.ToString()) }); + + IPrincipal user = CreateClaimsPrincipalFromClaimsIdentity(claimsIdentity); + + sut.ControllerContext = CreateControllerContextFromPrincipal(user); + } + + private IPrincipal CreateClaimsPrincipalFromClaimsIdentity(ClaimsIdentity claimsIdentity) + { + // see: https://stackoverflow.com/a/1784417/41236 + claimsPrincipalMock = new Mock(); + claimsPrincipalMock.SetupGet(p => p.Identities) + .Returns(new List { claimsIdentity }); + + // see: https://stackoverflow.com/a/1783704/41236 + IPrincipal user = claimsPrincipalMock.Object; + return user; + } + + private void BindModelToController(T model) + { + // see: https://stackoverflow.com/a/5580363/41236 + var modelBinder = new ModelBindingContext + { + ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(T)), + ValueProvider = new NameValueCollectionValueProvider(new NameValueCollection(), CultureInfo.InvariantCulture) + }; + var binder = new DefaultModelBinder().BindModel(new ControllerContext(), modelBinder); + sut.ModelState.Clear(); + sut.ModelState.Merge(modelBinder.ModelState); + } + + private ControllerContext CreateControllerContext() + { + return CreateControllerContextFromPrincipal(new Mock().Object); + } + + protected ControllerContext CreateControllerContextFromPrincipal(IPrincipal user) + { + httpContextMock = new Mock(); + httpContextMock.SetupGet(ctx => ctx.User).Returns(user); + + responseMock = new Mock(); + httpContextMock.SetupGet(c => c.Response) + .Returns(responseMock.Object); + + var controllerCtx = new ControllerContext + { + HttpContext = httpContextMock.Object + }; + return controllerCtx; + } + + private static ViewResult AssertAndGetViewResult(ActionResult result) + { + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + + return result as ViewResult; + } + + private static RedirectToRouteResult AssertAndGetRedirectToRouteResult(ActionResult result) + { + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult)); + + return result as RedirectToRouteResult; + } + + private static void AssertRedirectToHomeUnauthorized(ActionResult result) + { + RedirectToRouteResult redirectToRouteResult = AssertAndGetRedirectToRouteResult(result); + + Assert.IsNotNull(redirectToRouteResult); + Assert.AreEqual("Home", redirectToRouteResult.RouteValues["controller"]); + Assert.AreEqual("Unauthorized", redirectToRouteResult.RouteValues["action"]); + } + + private void SetupCookiesCollectionToHttpResponse() + { + HttpCookieCollection cookies = new HttpCookieCollection(); + responseMock.SetupGet(r => r.Cookies) + .Returns(cookies); + } + + private static void ArrangeUserConfiguration() + { + Mock pathResolverMock = new Mock(); + pathResolverMock.Setup(p => p.Resolve(It.IsAny())) + .Returns("."); + pathResolverMock.Setup(p => p.ResolveWithConfiguration(It.IsAny())) + .Returns("test.config"); + UserConfiguration.PathResolver = pathResolverMock.Object; + } + + private static void ReinitializeStaticClass(Type type) + { + // This code allows reinitializing a static class + // see: https://stackoverflow.com/a/51758748/41236 + // + type.TypeInitializer.Invoke(null, null); + } + + private Controller sut; + private Mock claimsPrincipalMock; + private Mock httpContextMock; + private Mock responseMock; + } +} diff --git a/Bonobo.Git.Server.Test/packages.config b/Bonobo.Git.Server.Test/packages.config index e12b1e08..9fd4b140 100644 --- a/Bonobo.Git.Server.Test/packages.config +++ b/Bonobo.Git.Server.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/Bonobo.Git.Server.sln b/Bonobo.Git.Server.sln index bf06ec7e..cada4a58 100644 --- a/Bonobo.Git.Server.sln +++ b/Bonobo.Git.Server.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31025.194 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonobo.Git.Server", "Bonobo.Git.Server\Bonobo.Git.Server.csproj", "{6129B3FE-B282-4F6F-8836-8AF66602F8DA}" EndProject