diff --git a/src/TeamUp.Common/Result/Extensions/ResultExtensions.And.cs b/src/TeamUp.Common/Result/Extensions/ResultExtensions.And.cs index 485c12c..22689e3 100644 --- a/src/TeamUp.Common/Result/Extensions/ResultExtensions.And.cs +++ b/src/TeamUp.Common/Result/Extensions/ResultExtensions.And.cs @@ -7,7 +7,7 @@ public static partial class ResultExtensions if (self.IsFailure) return self.Error; - return (self.Value!, func()); + return (self.Value, func()); } public static Result<(TFirst, TSecond)> And(this Result self, Func> func) @@ -19,7 +19,7 @@ public static partial class ResultExtensions if (result.IsFailure) return result.Error; - return (self.Value!, result.Value!); + return (self.Value, result.Value); } public static Result<(TFirst, TSecond)> And(this Result self, Func> func) @@ -27,11 +27,11 @@ public static partial class ResultExtensions if (self.IsFailure) return self.Error; - var result = func(self.Value!); + var result = func(self.Value); if (result.IsFailure) return result.Error; - return (self.Value!, result.Value!); + return (self.Value, result.Value); } public static async Task> AndAsync(this Result self, Func> func) @@ -39,7 +39,7 @@ public static partial class ResultExtensions if (self.IsFailure) return self.Error; - var result = await func(self.Value!); - return (self.Value!, result); + var result = await func(self.Value); + return (self.Value, result); } } diff --git a/src/TeamUp.Common/Result/Extensions/ResultExtensions.Ensure.cs b/src/TeamUp.Common/Result/Extensions/ResultExtensions.Ensure.cs index 33a3e0f..ca33d84 100644 --- a/src/TeamUp.Common/Result/Extensions/ResultExtensions.Ensure.cs +++ b/src/TeamUp.Common/Result/Extensions/ResultExtensions.Ensure.cs @@ -27,7 +27,7 @@ public static Result Ensure(this Result self, Ru if (self.IsFailure) return self; - if (!rule(self.Value!)) + if (!rule(self.Value)) return error; return self; @@ -40,7 +40,7 @@ public static Result Ensure(this Result self, par foreach (var rule in rules) { - var result = rule.Apply(self.Value!); + var result = rule.Apply(self.Value); if (result.IsFailure) return result; } @@ -78,7 +78,7 @@ public static Result EnsureNotNull(this Resul if (self.IsFailure) return self.Error; - if (selector(self.Value!) is null) + if (selector(self.Value) is null) return error; return self.Value; @@ -100,7 +100,7 @@ public static Result EnsureNotNull(this Resul if (self.IsFailure) return self; - return rule.Apply(self.Value!); + return rule.Apply(self.Value); } public static async Task> Ensure(this Task> selfTask, Rule rule, TError error) where TError : ErrorBase diff --git a/src/TeamUp.Common/Result/Extensions/ResultExtensions.Tap.cs b/src/TeamUp.Common/Result/Extensions/ResultExtensions.Tap.cs index 3feaccd..26719ed 100644 --- a/src/TeamUp.Common/Result/Extensions/ResultExtensions.Tap.cs +++ b/src/TeamUp.Common/Result/Extensions/ResultExtensions.Tap.cs @@ -7,7 +7,7 @@ public static Result Tap(this Result self, Action> TapAsync(this Result se if (self.IsFailure) return self; - await asyncFunc(self.Value!); + await asyncFunc(self.Value); return self; } diff --git a/src/TeamUp.Common/Result/Extensions/ResultExtensions.Then.cs b/src/TeamUp.Common/Result/Extensions/ResultExtensions.Then.cs index 3b11b7e..a07a20f 100644 --- a/src/TeamUp.Common/Result/Extensions/ResultExtensions.Then.cs +++ b/src/TeamUp.Common/Result/Extensions/ResultExtensions.Then.cs @@ -7,7 +7,7 @@ public static Result Then(this Result self, Func(this Result self, Func mapper) @@ -15,7 +15,7 @@ public static Result Then(this Result self, Func if (self.IsFailure) return self.Error; - return mapper(self.Value!); + return mapper(self.Value); } public static Result Then(this Result self, Func> mapper) @@ -23,7 +23,7 @@ public static Result Then(this Result self, Func> ThenAsync(this Result self, Func> asyncMapper) @@ -31,7 +31,7 @@ public static async Task> ThenAsync(this Result> Then(this Task> selfTask, Func mapper) @@ -67,7 +67,7 @@ public static async Task> ThenAsync(this Res if (self.IsFailure) return self.Error; - return await asyncMapper(self.Value!.Item1, self.Value.Item2); + return await asyncMapper(self.Value.Item1, self.Value.Item2); } public static async Task> ThenAsync(this Task> selfTask, Func> asyncMapper) @@ -81,7 +81,7 @@ public static async Task> ThenAsync(this Res if (self.IsFailure) return self.Error; - return await asyncMapper(self.Value!.Item1, self.Value.Item2); + return await asyncMapper(self.Value.Item1, self.Value.Item2); } public static async Task> ThenAsync(this Task> selfTask, Func>> asyncMapper) diff --git a/src/TeamUp.Common/Result/Result.cs b/src/TeamUp.Common/Result/Result.cs index c166f30..8ce4151 100644 --- a/src/TeamUp.Common/Result/Result.cs +++ b/src/TeamUp.Common/Result/Result.cs @@ -33,7 +33,7 @@ public sealed class Result public bool IsFailure => !IsSuccess; private readonly TValue? _value; - public TValue? Value => IsSuccess ? _value : + public TValue Value => IsSuccess ? _value! : throw new InvalidOperationException("Value of failure result cannot be accessed."); private readonly ErrorBase? _error = null; diff --git a/tests/TeamUp.EndToEndTests/DataGenerators/TeamGenerator.cs b/tests/TeamUp.EndToEndTests/DataGenerators/TeamGenerator.cs index a506159..7c99c9d 100644 --- a/tests/TeamUp.EndToEndTests/DataGenerators/TeamGenerator.cs +++ b/tests/TeamUp.EndToEndTests/DataGenerators/TeamGenerator.cs @@ -18,12 +18,12 @@ public sealed class TeamGenerator : BaseGenerator public static readonly Faker EmptyTeam = new Faker(binder: TeamBinder) .UsePrivateConstructor() - .RuleFor(t => t.Id, f => TeamId.FromGuid(f.Random.Uuid())) + .RuleFor(t => t.Id, f => TeamId.FromGuid(f.Random.Guid())) .RuleFor(t => t.Name, GenerateValidTeamName()); public static readonly Faker EmptyTeamMember = new Faker(binder: TeamMemberBinder) .UsePrivateConstructor() - .RuleFor(tm => tm.Id, f => TeamMemberId.FromGuid(f.Random.Uuid())); + .RuleFor(tm => tm.Id, f => TeamMemberId.FromGuid(f.Random.Guid())); public static readonly Faker ValidUpsertEventTypeRequest = new Faker() .RuleFor(r => r.Name, f => f.Random.AlphaNumeric(10)) @@ -54,6 +54,21 @@ public static Team GenerateTeamWith(User user, TeamRole role, List members }; } + public static Team GenerateTeamWith( + User user1, TeamRole role1, + User user2, TeamRole role2, + List members) + { + var hasOwner = (role1 != TeamRole.Owner && role2 != TeamRole.Owner && members.Count == 0); + hasOwner.Should().Be(false, "team has to have exactly 1 owner"); + + return (role1, role2) switch + { + (TeamRole.Owner, _) or (_, TeamRole.Owner) => GenerateTeamWith(members, (user1, role1), (user2, role2)), + _ => GenerateTeamWith(members[1..], (members.First(), TeamRole.Owner), (user1, role1), (user2, role2)) + }; + } + public static Team GenerateTeamWith(User owner, List members, params (User User, TeamRole Role)[] userMembers) => GenerateTeamWith(members, userMembers.Concat([(owner, TeamRole.Owner)]).ToArray()); diff --git a/tests/TeamUp.EndToEndTests/DataGenerators/UserGenerator.cs b/tests/TeamUp.EndToEndTests/DataGenerators/UserGenerator.cs index 8c8cb0f..9fb2697 100644 --- a/tests/TeamUp.EndToEndTests/DataGenerators/UserGenerator.cs +++ b/tests/TeamUp.EndToEndTests/DataGenerators/UserGenerator.cs @@ -21,6 +21,10 @@ public sealed class UserGenerator : BaseGenerator .RuleFor(u => u.Password, new Password()) .RuleFor(u => u.Status, UserStatus.Activated); + public static readonly Faker NotActivatedUser = EmptyUser + .RuleFor(u => u.Password, new Password()) + .RuleFor(u => u.Status, UserStatus.NotActivated); + public static User GenerateUser(Password password, UserStatus status) { return EmptyUser diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Invitations/InviteUserTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Invitations/InviteUserTests.cs index ffe8284..568228b 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Invitations/InviteUserTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Invitations/InviteUserTests.cs @@ -10,13 +10,13 @@ public InviteUserTests(TeamApiWebApplicationFactory appFactory) : base(appFactor [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] [InlineData(TeamRole.Owner)] - public async Task InviteUser_ThatIsActivated_AsCoordinatorOrHigher_Should_CreateInvitationInDatabase_And_SendInvitationEmail(TeamRole teamRole) + public async Task InviteUser_ThatIsActivated_AsCoordinatorOrHigher_Should_CreateInvitationInDatabase_And_SendInvitationEmail(TeamRole initiatorRole) { //arrange var initiatorUser = UserGenerator.ActivatedUser.Generate(); var targetUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, members); await UseDbContextAsync(dbContext => { @@ -57,12 +57,12 @@ await UseDbContextAsync(dbContext => [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] [InlineData(TeamRole.Owner)] - public async Task InviteUser_ThatIsNotRegistered_AsCoordinatorOrHigher_Should_CreateInvitationInDatabase_And_GenerateNewUser_And_SendInvitationEmail(TeamRole teamRole) + public async Task InviteUser_ThatIsNotRegistered_AsCoordinatorOrHigher_Should_CreateInvitationInDatabase_And_GenerateNewUser_And_SendInvitationEmail(TeamRole initiatorRole) { //arrange var initiatorUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, members); await UseDbContextAsync(dbContext => { @@ -111,12 +111,12 @@ await UseDbContextAsync(async dbContext => [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] [InlineData(TeamRole.Owner)] - public async Task InviteUser_ThatIsAlreadyInTeam_AsCoordinatorOrHigher_Should_ResultInBadRequest(TeamRole teamRole) + public async Task InviteUser_ThatIsAlreadyInTeam_AsCoordinatorOrHigher_Should_ResultInBadRequest_DomainError(TeamRole initiatorRole) { //arrange var initiatorUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, members); var targetUser = members.First(); await UseDbContextAsync(dbContext => @@ -184,13 +184,13 @@ await UseDbContextAsync(dbContext => [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] [InlineData(TeamRole.Owner)] - public async Task InviteUser_ThatIsAlreadyInvited_AsCoordinatorOrHigher_Should_ResultInConflict(TeamRole teamRole) + public async Task InviteUser_ThatIsAlreadyInvited_AsCoordinatorOrHigher_Should_ResultInConflict(TeamRole initiatorRole) { //arrange var initiatorUser = UserGenerator.ActivatedUser.Generate(); var targetUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, members); var invitation = InvitationGenerator.GenerateInvitation(targetUser.Id, team.Id, DateTime.UtcNow); await UseDbContextAsync(dbContext => @@ -289,7 +289,7 @@ await UseDbContextAsync(dbContext => [Theory] [ClassData(typeof(InvitationGenerator.InvalidInviteUserRequest))] - public async Task InviteUser_WithInvalidRequest_Should_ResultInBadRequest(InvalidRequest request) + public async Task InviteUser_WithInvalidRequest_Should_ResultInBadRequest_ValidationErrors(InvalidRequest request) { //arrange var initiatorUser = UserGenerator.ActivatedUser.Generate(); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeNicknameTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeNicknameTests.cs index bfafb76..cf29516 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeNicknameTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeNicknameTests.cs @@ -1,7 +1,6 @@ using Microsoft.EntityFrameworkCore; using TeamUp.Contracts.Teams; -using TeamUp.Domain.Aggregates.Teams; namespace TeamUp.EndToEndTests.EndpointTests.Teams; @@ -9,62 +8,21 @@ public sealed class ChangeNicknameTests : BaseTeamTests { public ChangeNicknameTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } - [Fact] - public async Task ChangeNickname_ToValidNickname_AsOwner_Should_UpdateNicknameInDatabase() - { - //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(owner, members); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(owner); - dbContext.Users.AddRange(members); - dbContext.Teams.Add(team); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(owner); - - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == owner.Id)!.Id; - var request = new ChangeNicknameRequest - { - Nickname = TeamGenerator.GenerateValidNickname() - }; - - //act - var response = await Client.PutAsJsonAsync($"/api/v1/teams/{team.Id.Value}/nickname", request); - - //assert - response.Should().Be200Ok(); - - var member = await UseDbContextAsync(dbContext => - { - return dbContext - .Set() - .SingleOrDefaultAsync(member => member.Id == targetMemberId); - }); - - member.ShouldNotBeNull(); - member.Nickname.Should().Be(request.Nickname); - } - [Theory] [InlineData(TeamRole.Member)] [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] - public async Task ChangeNickname_ToValidNickname_AsAdminOrLower_Should_UpdateNicknameInDatabase(TeamRole teamRole) + [InlineData(TeamRole.Owner)] + public async Task ChangeNickname_ToValidNickname_AsTeamMember_Should_UpdateNicknameInDatabase(TeamRole teamRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, teamRole)); + var members = UserGenerator.ActivatedUser.Generate(19); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser]); + dbContext.Users.Add(initiatorUser); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -72,7 +30,7 @@ await UseDbContextAsync(dbContext => Authenticate(initiatorUser); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == initiatorUser.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == initiatorUser.Id).Id; var request = new ChangeNicknameRequest { Nickname = TeamGenerator.GenerateValidNickname() @@ -163,7 +121,7 @@ await UseDbContextAsync(dbContext => [InlineData("")] [InlineData("x")] [InlineData("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")] - public async Task ChangeNickname_ToInvalidName_Should_ResultInBadRequest(string invalidName) + public async Task ChangeNickname_ToInvalidName_Should_ResultInBadRequest_ValidationErrors(string invalidName) { //arrange var owner = UserGenerator.ActivatedUser.Generate(); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeOwnershipTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeOwnershipTests.cs index 19ec177..15645d6 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeOwnershipTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/ChangeOwnershipTests.cs @@ -130,7 +130,7 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task ChangeOwnership_WhenNotMember_Should_ResultInForbidden() + public async Task ChangeOwnership_WhenNotMemberOfTeam_Should_ResultInForbidden() { //arrange var owner = UserGenerator.ActivatedUser.Generate(); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateEventTypeTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateEventTypeTests.cs index 044df01..4be1bc1 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateEventTypeTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateEventTypeTests.cs @@ -6,61 +6,20 @@ public sealed class CreateEventTypeTests : BaseTeamTests { public CreateEventTypeTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } - [Fact] - public async Task CreateEventType_AsOwner_Should_AddNewEventTypeToTeamInDatabase() - { - //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(owner, members); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(owner); - dbContext.Users.AddRange(members); - dbContext.Teams.Add(team); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(owner); - - var request = TeamGenerator.ValidUpsertEventTypeRequest.Generate(); - - //act - var response = await Client.PostAsJsonAsync($"/api/v1/teams/{team.Id.Value}/event-types", request); - - //assert - response.Should().Be201Created(); - - var eventTypeId = await response.Content.ReadFromJsonAsync(); - eventTypeId.ShouldNotBeNull(); - - await UseDbContextAsync(async dbContext => - { - var createdEventType = await dbContext.Set().FindAsync(eventTypeId); - createdEventType.Should().BeEquivalentTo(request); - - var updatedTeam = await dbContext.Teams.FindAsync(team.Id); - updatedTeam.ShouldNotBeNull(); - updatedTeam.EventTypes.Should().ContainSingle(); - updatedTeam.EventTypes[0].Should().BeEquivalentTo(createdEventType); - }); - } - [Theory] [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] - public async Task CreateEventType_AsAdminOrCoordinator_Should_AddNewEventTypeToTeamInDatabase(TeamRole teamRole) + [InlineData(TeamRole.Owner)] + public async Task CreateEventType_AsCoordinatorOrHigher_Should_AddNewEventTypeToTeamInDatabase(TeamRole teamRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, teamRole)); + var members = UserGenerator.ActivatedUser.Generate(19); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser]); + dbContext.Users.Add(initiatorUser); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -182,7 +141,7 @@ await UseDbContextAsync(dbContext => [Theory] [ClassData(typeof(TeamGenerator.InvalidUpsertEventTypeRequest))] - public async Task CreateEventType_WithInvalidParameters_AsOwner_Should_ResultInBadRequest(InvalidRequest request) + public async Task CreateEventType_WithInvalidParameters_AsOwner_Should_ResultInBadRequest_ValidationErrors(InvalidRequest request) { //arrange var owner = UserGenerator.ActivatedUser.Generate(); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateTeamTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateTeamTests.cs index ece6931..774c855 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateTeamTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/CreateTeamTests.cs @@ -9,7 +9,7 @@ public sealed class CreateTeamTests : BaseTeamTests public CreateTeamTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } [Fact] - public async Task CreateTeam_Should_CreateNewTeamInDatabase_WithOneTeamMember() + public async Task CreateTeam_Should_CreateNewTeamInDatabase_WithOneTeamOwner() { //arrange var user = UserGenerator.ActivatedUser.Generate(); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/GetTeamTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/GetTeamTests.cs index 19f6462..6226c62 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/GetTeamTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/GetTeamTests.cs @@ -31,60 +31,27 @@ await UseDbContextAsync(dbContext => problemDetails.ShouldContainError(TeamErrors.TeamNotFound); } - [Fact] - public async Task GetTeam_WithOneMember_AsTeamOwner_Should_ReturnTeamFromDatabase() - { - //arrange - var user = UserGenerator.ActivatedUser.Generate(); - var team = TeamGenerator.GenerateTeamWithOwner(user); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(user); - dbContext.Teams.Add(team); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(user); - - //act - var response = await Client.GetAsync($"/api/v1/teams/{team.Id.Value}"); - - //assert - response.Should().Be200Ok(); - - var teamResponse = await response.Content.ReadFromJsonAsync(); - teamResponse.ShouldNotBeNull(); - team.Should().BeEquivalentTo(teamResponse, options => - { - return options - .ExcludingMissingMembers() - .IgnoringCyclicReferences(); - }); - } - [Theory] [InlineData(TeamRole.Member)] [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] - public async Task GetTeam_With20Members_AsAdminOrLower_Should_ReturnTeamFromDatabase(TeamRole asRole) + [InlineData(TeamRole.Owner)] + public async Task GetTeam_AsTeamMember_Should_ReturnTeam(TeamRole initiatorRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var user = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (user, asRole)); + var initiatorUser = UserGenerator.ActivatedUser.Generate(); + var members = UserGenerator.ActivatedUser.Generate(19); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.Add(owner); - dbContext.Users.Add(user); + dbContext.Users.Add(initiatorUser); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); }); - Authenticate(user); + Authenticate(initiatorUser); //act var response = await Client.GetAsync($"/api/v1/teams/{team.Id.Value}"); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/RemoveTeamMemberTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/RemoveTeamMemberTests.cs index fe58418..e01848e 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/RemoveTeamMemberTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/RemoveTeamMemberTests.cs @@ -5,65 +5,25 @@ public sealed class RemoveTeamMemberTests : BaseTeamTests public RemoveTeamMemberTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } [Theory] - [InlineData(TeamRole.Member)] - [InlineData(TeamRole.Coordinator)] - [InlineData(TeamRole.Admin)] - public async Task RemoveTeamMember_AsOwner_WhenRemovingAdminOrLower_Should_RemoveTeamMemberFromTeam(TeamRole targetMemberRole) + [InlineData(TeamRole.Admin, TeamRole.Member)] + [InlineData(TeamRole.Admin, TeamRole.Coordinator)] + [InlineData(TeamRole.Admin, TeamRole.Admin)] + [InlineData(TeamRole.Owner, TeamRole.Member)] + [InlineData(TeamRole.Owner, TeamRole.Coordinator)] + [InlineData(TeamRole.Owner, TeamRole.Admin)] + public async Task RemoveTeamMember_AsOwnerOrAdmin_WhenRemovingAdminOrLower_Should_RemoveTeamMemberFromTeamInDatabase(TeamRole initiatorTeamRole, TeamRole targetMemberRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var targetUser = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (targetUser, targetMemberRole)); - - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(owner); - dbContext.Users.Add(targetUser); - dbContext.Users.AddRange(members); - dbContext.Teams.Add(team); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(owner); - - //act - var response = await Client.DeleteAsync($"/api/v1/teams/{team.Id.Value}/{targetMemberId.Value}"); - - //assert - response.Should().Be200Ok(); - - await UseDbContextAsync(async dbContext => - { - var targetMember = await dbContext.Set().FindAsync(targetMemberId); - targetMember.Should().BeNull("this member has been removed"); - - var targetMemberUser = await dbContext.Users.FindAsync(targetUser.Id); - targetMemberUser.ShouldNotBeNull(); - targetMemberUser.Should().BeEquivalentTo(targetUser); - }); - } - - [Theory] - [InlineData(TeamRole.Member)] - [InlineData(TeamRole.Coordinator)] - [InlineData(TeamRole.Admin)] - public async Task RemoveTeamMember_AsAdmin_WhenRemovingAdminOrLower_Should_RemoveTeamMemberFromTeam(TeamRole targetMemberRole) - { - //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); var targetUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, TeamRole.Admin), (targetUser, targetMemberRole)); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorTeamRole, targetUser, targetMemberRole, members); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == targetUser.Id).Id; await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser, targetUser]); + dbContext.Users.AddRange([initiatorUser, targetUser]); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -92,25 +52,24 @@ await UseDbContextAsync(async dbContext => [InlineData(TeamRole.Member)] [InlineData(TeamRole.Coordinator)] [InlineData(TeamRole.Admin)] - public async Task RemoveTeamMember_AsAdminOrLower_WhenRemovingMyself_Should_RemoveTeamMemberFromTeam(TeamRole memberRole) + public async Task RemoveTeamMember_AsAdminOrLower_WhenRemovingMyself_Should_RemoveTeamMemberFromTeamInDatabase(TeamRole teamRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var user = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (user, memberRole)); + var initiatorUser = UserGenerator.ActivatedUser.Generate(); + var members = UserGenerator.ActivatedUser.Generate(19); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == user.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == initiatorUser.Id).Id; await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, user]); + dbContext.Users.Add(initiatorUser); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); }); - Authenticate(user); + Authenticate(initiatorUser); //act var response = await Client.DeleteAsync($"/api/v1/teams/{team.Id.Value}/{targetMemberId.Value}"); @@ -123,9 +82,9 @@ await UseDbContextAsync(async dbContext => var targetMember = await dbContext.Set().FindAsync(targetMemberId); targetMember.Should().BeNull("this member has been removed"); - var targetMemberUser = await dbContext.Users.FindAsync(user.Id); + var targetMemberUser = await dbContext.Users.FindAsync(initiatorUser.Id); targetMemberUser.ShouldNotBeNull(); - targetMemberUser.Should().BeEquivalentTo(user); + targetMemberUser.Should().BeEquivalentTo(initiatorUser); }); } @@ -141,7 +100,7 @@ public async Task RemoveTeamMember_AsCoordinatorOrMember_WhenRemovingTeamMember_ var members = UserGenerator.ActivatedUser.Generate(18); var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, memberRole), (targetUser, TeamRole.Member)); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == targetUser.Id).Id; await UseDbContextAsync(dbContext => { @@ -164,14 +123,14 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task RemoveTeamMember_AsOwner_WhenRemovingMyself_Should_ResultInDomainError_BadRequest() + public async Task RemoveTeamMember_AsOwner_WhenRemovingMyself_Should_ResultInBadRequest_DomainError() { //arrange var owner = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); var team = TeamGenerator.GenerateTeamWith(owner, members); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == owner.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == owner.Id).Id; await UseDbContextAsync(dbContext => { @@ -194,7 +153,7 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task RemoveTeamMember_AsAdmin_WhenRemovingTeamOwner_Should_ResultInDomainError_BadRequest() + public async Task RemoveTeamMember_AsAdmin_WhenRemovingTeamOwner_Should_ResultInBadRequest_DomainError() { //arrange var owner = UserGenerator.ActivatedUser.Generate(); @@ -202,7 +161,7 @@ public async Task RemoveTeamMember_AsAdmin_WhenRemovingTeamOwner_Should_ResultIn var members = UserGenerator.ActivatedUser.Generate(18); var team = TeamGenerator.GenerateTeamWith(owner, members, (user, TeamRole.Admin)); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == owner.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == owner.Id).Id; await UseDbContextAsync(dbContext => { @@ -255,7 +214,7 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task RemoveTeamMember_FromTeamThatDoesNotExist_Should_ResultInNotFound() + public async Task RemoveTeamMember_FromUnExistingTeam_Should_ResultInNotFound() { //arrange var user = UserGenerator.ActivatedUser.Generate(); @@ -290,7 +249,7 @@ public async Task RemoveTeamMember_WhenNotMemberOfTeam_Should_ResultInForbidden( var members = UserGenerator.ActivatedUser.Generate(19); var team = TeamGenerator.GenerateTeamWith(owner, members); - var targetMemberId = team.Members.FirstOrDefault(member => member.Role != TeamRole.Owner)!.Id; + var targetMemberId = team.Members.First(member => member.Role != TeamRole.Owner).Id; await UseDbContextAsync(dbContext => { diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamMemberRoleTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamMemberRoleTests.cs index 432e9c8..814e644 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamMemberRoleTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamMemberRoleTests.cs @@ -9,79 +9,29 @@ public sealed class UpdateTeamMemberRoleTests : BaseTeamTests public UpdateTeamMemberRoleTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } [Theory] - [InlineData(TeamRole.Member, TeamRole.Admin)] - [InlineData(TeamRole.Member, TeamRole.Coordinator)] - [InlineData(TeamRole.Coordinator, TeamRole.Admin)] - [InlineData(TeamRole.Coordinator, TeamRole.Member)] - [InlineData(TeamRole.Admin, TeamRole.Coordinator)] - [InlineData(TeamRole.Admin, TeamRole.Member)] - public async Task UpdateTeamRole_OfAdminOrLower_AsOwner_Should_UpdateTeamMemberRoleInDatabase(TeamRole targetMemberRole, TeamRole newRole) - { - //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var targetUser = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (targetUser, targetMemberRole)); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.AddRange([owner, targetUser]); - dbContext.Users.AddRange(members); - dbContext.Teams.Add(team); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(owner); - - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; - var request = new UpdateTeamRoleRequest - { - Role = newRole - }; - - //act - var response = await Client.PutAsJsonAsync($"/api/v1/teams/{team.Id.Value}/{targetMemberId.Value}/role", request); - - //assert - response.Should().Be200Ok(); - - var teamMembers = await UseDbContextAsync(dbContext => - { - return dbContext - .Set() - .Where(teamMember => teamMember.TeamId == team.Id) - .ToListAsync(); - }); - - teamMembers.Should().ContainSingle(member => member.Role == TeamRole.Owner); - - var updatedMember = teamMembers.SingleOrDefault(member => member.UserId == targetUser.Id); - updatedMember.ShouldNotBeNull(); - updatedMember.Role.Should().Be(newRole); - - teamMembers.Except([updatedMember]) - .Should().OnlyContain(member => TeamContainsMemberWithSameRole(team, member)); - } - - [Theory] - [InlineData(TeamRole.Member, TeamRole.Admin)] - [InlineData(TeamRole.Member, TeamRole.Coordinator)] - [InlineData(TeamRole.Coordinator, TeamRole.Admin)] - [InlineData(TeamRole.Coordinator, TeamRole.Member)] - [InlineData(TeamRole.Admin, TeamRole.Coordinator)] - [InlineData(TeamRole.Admin, TeamRole.Member)] - public async Task UpdateTeamRole_OfAdminOrLower_AsAdmin_Should_UpdateTeamMemberRoleInDatabase(TeamRole targetMemberRole, TeamRole newRole) + [InlineData(TeamRole.Admin, TeamRole.Member, TeamRole.Admin)] + [InlineData(TeamRole.Admin, TeamRole.Member, TeamRole.Coordinator)] + [InlineData(TeamRole.Admin, TeamRole.Coordinator, TeamRole.Admin)] + [InlineData(TeamRole.Admin, TeamRole.Coordinator, TeamRole.Member)] + [InlineData(TeamRole.Admin, TeamRole.Admin, TeamRole.Coordinator)] + [InlineData(TeamRole.Admin, TeamRole.Admin, TeamRole.Member)] + [InlineData(TeamRole.Owner, TeamRole.Member, TeamRole.Admin)] + [InlineData(TeamRole.Owner, TeamRole.Member, TeamRole.Coordinator)] + [InlineData(TeamRole.Owner, TeamRole.Coordinator, TeamRole.Admin)] + [InlineData(TeamRole.Owner, TeamRole.Coordinator, TeamRole.Member)] + [InlineData(TeamRole.Owner, TeamRole.Admin, TeamRole.Coordinator)] + [InlineData(TeamRole.Owner, TeamRole.Admin, TeamRole.Member)] + public async Task UpdateTeamRole_OfAdminOrLower_AsOwnerOrAdmin_Should_UpdateTeamMemberRoleInDatabase(TeamRole initiatorRole, TeamRole targetRole, TeamRole newRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); var targetUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, TeamRole.Admin), (targetUser, targetMemberRole)); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, targetUser, targetRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser, targetUser]); + dbContext.Users.AddRange([initiatorUser, targetUser]); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -89,7 +39,7 @@ await UseDbContextAsync(dbContext => Authenticate(initiatorUser); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == targetUser.Id).Id; var request = new UpdateTeamRoleRequest { Role = newRole @@ -132,18 +82,17 @@ await UseDbContextAsync(dbContext => [InlineData(TeamRole.Member, TeamRole.Coordinator, TeamRole.Member)] [InlineData(TeamRole.Member, TeamRole.Admin, TeamRole.Coordinator)] [InlineData(TeamRole.Member, TeamRole.Admin, TeamRole.Member)] - public async Task UpdateTeamRole_OfAdminOrLower_AsCoordinatorOrMember_Should_ResultInForbidden(TeamRole role, TeamRole targetMemberRole, TeamRole newRole) + public async Task UpdateTeamRole_OfAdminOrLower_AsCoordinatorOrMember_Should_ResultInForbidden(TeamRole initiatorRole, TeamRole targetRole, TeamRole newRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); var targetUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(18); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, role), (targetUser, targetMemberRole)); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, targetUser, targetRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser, targetUser]); + dbContext.Users.AddRange([initiatorUser, targetUser]); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -151,7 +100,7 @@ await UseDbContextAsync(dbContext => Authenticate(initiatorUser); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == targetUser.Id).Id; var request = new UpdateTeamRoleRequest { Role = newRole @@ -168,57 +117,22 @@ await UseDbContextAsync(dbContext => } [Theory] - [InlineData(TeamRole.Member)] - [InlineData(TeamRole.Coordinator)] - [InlineData(TeamRole.Admin)] - public async Task UpdateTeamRole_OfOwner_AsOwner_Should_ResultInDomainError_BadRequest(TeamRole newRole) - { - //arrange - var owner = UserGenerator.ActivatedUser.Generate(); - var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(owner, members); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(owner); - dbContext.Users.AddRange(members); - dbContext.Teams.Add(team); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(owner); - - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == owner.Id)!.Id; - var request = new UpdateTeamRoleRequest - { - Role = newRole - }; - - //act - var response = await Client.PutAsJsonAsync($"/api/v1/teams/{team.Id.Value}/{targetMemberId.Value}/role", request); - - //assert - response.Should().Be400BadRequest(); - - var problemDetails = await response.Content.ReadFromJsonAsync(); - problemDetails.ShouldContainError(TeamErrors.CannotChangeTeamOwnersRole); - } - - [Theory] - [InlineData(TeamRole.Member)] - [InlineData(TeamRole.Coordinator)] - [InlineData(TeamRole.Admin)] - public async Task UpdateTeamRole_OfOwner_AsAdmin_Should_ResultInDomainError_BadRequest(TeamRole newRole) + [InlineData(TeamRole.Admin, TeamRole.Member)] + [InlineData(TeamRole.Admin, TeamRole.Coordinator)] + [InlineData(TeamRole.Admin, TeamRole.Admin)] + [InlineData(TeamRole.Owner, TeamRole.Member)] + [InlineData(TeamRole.Owner, TeamRole.Coordinator)] + [InlineData(TeamRole.Owner, TeamRole.Admin)] + public async Task UpdateTeamRole_OfOwner_AsOwnerOrAdmin_Should_ResultInBadRequest_DomainError(TeamRole initiatorRole, TeamRole newRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, TeamRole.Admin)); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, initiatorRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser]); + dbContext.Users.Add(initiatorUser); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -226,7 +140,7 @@ await UseDbContextAsync(dbContext => Authenticate(initiatorUser); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == owner.Id)!.Id; + var targetMemberId = team.Members.First(member => member.Role == TeamRole.Owner).Id; var request = new UpdateTeamRoleRequest { Role = newRole @@ -246,7 +160,7 @@ await UseDbContextAsync(dbContext => [InlineData((TeamRole)4)] [InlineData((TeamRole)99)] [InlineData((TeamRole)(-1))] - public async Task UpdateTeamRole_OfMember_ToInvalidValue_AsOwner_Should_ResultInBadRequest(TeamRole newRole) + public async Task UpdateTeamRole_OfMember_ToInvalidValue_AsOwner_Should_ResultInBadRequest_ValidationErrors(TeamRole newRole) { //arrange var owner = UserGenerator.ActivatedUser.Generate(); @@ -265,7 +179,7 @@ await UseDbContextAsync(dbContext => Authenticate(owner); - var targetMemberId = team.Members.FirstOrDefault(member => member.UserId == targetUser.Id)!.Id; + var targetMemberId = team.Members.First(member => member.UserId == targetUser.Id).Id; var request = new UpdateTeamRoleRequest { Role = newRole @@ -282,7 +196,7 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task UpdateTeamRole_OfMemberThatDoesNotExist_AsOwner_Should_ResultInNotFound() + public async Task UpdateTeamRole_OfUnExistingMember_AsOwner_Should_ResultInNotFound() { //arrange var owner = UserGenerator.ActivatedUser.Generate(); @@ -316,7 +230,7 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task UpdateTeamRole_InTeamThatDoesNotExist_Should_ResultInNotFound() + public async Task UpdateTeamRole_InUnExistingTeam_Should_ResultInNotFound() { //arrange var user = UserGenerator.ActivatedUser.Generate(); @@ -365,7 +279,7 @@ await UseDbContextAsync(dbContext => Authenticate(initiatorUser); - var targetMemberId = team.Members.FirstOrDefault(member => member.Role != TeamRole.Owner)!.Id; + var targetMemberId = team.Members.First(member => member.Role != TeamRole.Owner).Id; var request = new UpdateTeamRoleRequest { Role = TeamRole.Member diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamNameTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamNameTests.cs index fcf06d6..08623f7 100644 --- a/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamNameTests.cs +++ b/tests/TeamUp.EndToEndTests/EndpointTests/Teams/UpdateTeamNameTests.cs @@ -48,14 +48,13 @@ await UseDbContextAsync(dbContext => public async Task UpdateTeamName_AsAdminOrLower_Should_ResultInForbidden(TeamRole teamRole) { //arrange - var owner = UserGenerator.ActivatedUser.Generate(); var initiatorUser = UserGenerator.ActivatedUser.Generate(); var members = UserGenerator.ActivatedUser.Generate(19); - var team = TeamGenerator.GenerateTeamWith(owner, members, (initiatorUser, teamRole)); + var team = TeamGenerator.GenerateTeamWith(initiatorUser, teamRole, members); await UseDbContextAsync(dbContext => { - dbContext.Users.AddRange([owner, initiatorUser]); + dbContext.Users.Add(initiatorUser); dbContext.Users.AddRange(members); dbContext.Teams.Add(team); return dbContext.SaveChangesAsync(); @@ -79,7 +78,7 @@ await UseDbContextAsync(dbContext => } [Fact] - public async Task UpdateTeamName_ThatDoesNotExist_Should_ResultInForbidden() + public async Task UpdateTeamName_OfUnExistingTeam_Should_ResultInForbidden() { //arrange var user = UserGenerator.ActivatedUser.Generate(); @@ -146,7 +145,7 @@ await UseDbContextAsync(dbContext => [InlineData("")] [InlineData("x")] [InlineData("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")] - public async Task UpdateTeamName_WithInvalidTeamName_AsOwner_Should_ResultInBadRequest(string name) + public async Task UpdateTeamName_WithInvalidTeamName_AsOwner_Should_ResultInBadRequest_ValidationErrors(string name) { //arrange var owner = UserGenerator.ActivatedUser.Generate(); diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/ActivateAccountTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/ActivateAccountTests.cs new file mode 100644 index 0000000..daac7da --- /dev/null +++ b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/ActivateAccountTests.cs @@ -0,0 +1,29 @@ +namespace TeamUp.EndToEndTests.EndpointTests.UserAccess; + +public sealed class ActivateAccountTests : BaseUserAccessTests +{ + public ActivateAccountTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } + + [Fact] + public async Task ActivateAccount_Should_SetUserStatusAsActivatedInDatabase() + { + //arrange + var user = UserGenerator.NotActivatedUser.Generate(); + + await UseDbContextAsync(dbContext => + { + dbContext.Add(user); + return dbContext.SaveChangesAsync(); + }); + + //act + var response = await Client.PostAsJsonAsync($"/api/v1/users/{user.Id.Value}/activate", EmptyObject); + + //assert + response.Should().Be200Ok(); + + var activatedUser = await UseDbContextAsync(dbContext => dbContext.Users.FindAsync(user.Id)); + activatedUser.ShouldNotBeNull(); + activatedUser.Status.Should().Be(UserStatus.Activated); + } +} diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/BaseUserAccessTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/BaseUserAccessTests.cs new file mode 100644 index 0000000..83fc478 --- /dev/null +++ b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/BaseUserAccessTests.cs @@ -0,0 +1,6 @@ +namespace TeamUp.EndToEndTests.EndpointTests.UserAccess; + +public abstract class BaseUserAccessTests : BaseEndpointTests +{ + protected BaseUserAccessTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } +} diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/GetMyProfileTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/GetMyProfileTests.cs new file mode 100644 index 0000000..125990e --- /dev/null +++ b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/GetMyProfileTests.cs @@ -0,0 +1,43 @@ +using TeamUp.Contracts.Users; + +namespace TeamUp.EndToEndTests.EndpointTests.UserAccess; + +public sealed class GetMyProfileTests : BaseUserAccessTests +{ + public GetMyProfileTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } + + [Fact] + public async Task GetMyProfile_WhenUnauthenticated_Should_ResultInUnauthorized() + { + //arrange + //act + var response = await Client.GetAsync("/api/v1/users/my-profile"); + + //assert + response.Should().Be401Unauthorized(); + } + + [Fact] + public async Task GetMyProfile_AsExistingUser_Should_ReturnUserDetails() + { + //arrange + var user = UserGenerator.ActivatedUser.Generate(); + + await UseDbContextAsync(dbContext => + { + dbContext.Users.Add(user); + return dbContext.SaveChangesAsync(); + }); + + Authenticate(user); + + //act + var response = await Client.GetAsync("/api/v1/users/my-profile"); + + //assert + response.Should().Be200Ok(); + + var userResponse = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); + user.Should().BeEquivalentTo(userResponse, o => o.ExcludingMissingMembers()); + } +} diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/LoginTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/LoginTests.cs new file mode 100644 index 0000000..8f7bfc7 --- /dev/null +++ b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/LoginTests.cs @@ -0,0 +1,140 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; + +using TeamUp.Application.Users; +using TeamUp.Contracts.Users; + +namespace TeamUp.EndToEndTests.EndpointTests.UserAccess; + +public sealed class LoginTests : BaseUserAccessTests +{ + public LoginTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } + + [Fact] + public async Task Login_AsActivatedUser_Should_GenerateValidJwtToken() + { + //arrange + var passwordService = AppFactory.Services.GetRequiredService(); + + var rawPassword = UserGenerator.GenerateValidPassword(); + var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.Activated); + + await UseDbContextAsync(dbContext => + { + dbContext.Add(user); + return dbContext.SaveChangesAsync(); + }); + + var request = new LoginRequest + { + Email = user.Email, + Password = rawPassword + }; + + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); + + //assert + response.Should().Be200Ok(); + + var token = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); + token.Should().NotBeNullOrEmpty(); + + var handler = new JwtSecurityTokenHandler(); + var jwt = handler.ReadJwtToken(token); + + jwt.ShouldNotBeNull(); + jwt.ValidTo.Ticks.Should().BeGreaterThan(DateTime.UtcNow.Ticks); + jwt.Claims.Select(claim => (claim.Type, claim.Value)) + .Should() + .Contain([ + (ClaimTypes.NameIdentifier, user.Id.Value.ToString()), + (ClaimTypes.Name, user.Name), + (ClaimTypes.Email, user.Email) + ]); + } + + [Fact] + public async Task Login_AsInactivatedUser_Should_ResultInUnauthorized() + { + //arrange + var passwordService = AppFactory.Services.GetRequiredService(); + + var rawPassword = UserGenerator.GenerateValidPassword(); + var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.NotActivated); + + await UseDbContextAsync(dbContext => + { + dbContext.Add(user); + return dbContext.SaveChangesAsync(); + }); + + var request = new LoginRequest + { + Email = user.Email, + Password = rawPassword + }; + + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); + + //assert + response.Should().Be401Unauthorized(); + + var problemDetails = await response.Content.ReadFromJsonAsync(); + problemDetails.ShouldContainError(AuthenticationErrors.NotActivatedAccount); + } + + [Fact] + public async Task Login_AsUnExistingUser_Should_ResultInUnauthorized() + { + //arrange + + var request = new LoginRequest + { + Email = F.Internet.Email(), + Password = UserGenerator.GenerateValidPassword() + }; + + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); + + //assert + response.Should().Be401Unauthorized(); + + var problemDetails = await response.Content.ReadFromJsonAsync(); + problemDetails.ShouldContainError(AuthenticationErrors.InvalidCredentials); + } + + [Fact] + public async Task Login_WithIncorrectPassword_Should_ResultInUnauthorized() + { + //arrange + var passwordService = AppFactory.Services.GetRequiredService(); + + var rawPassword = UserGenerator.GenerateValidPassword(); + var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.Activated); + + await UseDbContextAsync(dbContext => + { + dbContext.Add(user); + return dbContext.SaveChangesAsync(); + }); + + var request = new LoginRequest + { + Email = user.Email, + Password = rawPassword + "x" + }; + + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); + + //assert + response.Should().Be401Unauthorized(); + + var problemDetails = await response.Content.ReadFromJsonAsync(); + problemDetails.ShouldContainError(AuthenticationErrors.InvalidCredentials); + } + +} diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/RegisterUserTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/RegisterUserTests.cs new file mode 100644 index 0000000..d2d8228 --- /dev/null +++ b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccess/RegisterUserTests.cs @@ -0,0 +1,79 @@ +using TeamUp.Contracts.Users; + +namespace TeamUp.EndToEndTests.EndpointTests.UserAccess; + +public sealed class RegisterUserTests : BaseUserAccessTests +{ + public RegisterUserTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } + + [Fact] + public async Task RegisterUser_Should_CreateNewUserInDatabase_And_SendActivationEmail() + { + //arrange + var request = UserGenerator.ValidRegisterUserRequest.Generate(); + + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/register", request); + + //assert + response.Should().Be201Created(); + + var userId = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); + userId.ShouldNotBeNull(); + + var user = await UseDbContextAsync(dbContext => dbContext.Users.FindAsync(userId)); + user.ShouldNotBeNull(); + + user.Name.Should().BeEquivalentTo(request.Name); + user.Email.Should().BeEquivalentTo(request.Email); + user.Password.Should().NotBeEquivalentTo(request.Password); + + await WaitForIntegrationEventsAsync(); //wait for email + Inbox.Should().Contain(email => email.EmailAddress == request.Email); + } + + [Fact] + public async Task RegisterUser_WithAlreadyUsedEmail_Should_ResultInConflict() + { + //arrange + var user = UserGenerator.ActivatedUser.Generate(); + + await UseDbContextAsync(dbContext => + { + dbContext.Users.Add(user); + return dbContext.SaveChangesAsync(); + }); + + var request = new RegisterUserRequest() + { + Email = user.Email, + Name = F.Internet.UserName(), + Password = UserGenerator.GenerateValidPassword(), + }; + + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/register", request); + + //assert + response.Should().Be409Conflict(); + + var problemDetails = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); + problemDetails.ShouldContainError(UserErrors.ConflictingEmail); + } + + [Theory] + [ClassData(typeof(UserGenerator.InvalidRegisterUserRequests))] + public async Task RegisterUser_WithInvalidProperties_Should_ResultInValidationErrors_BadRequest(InvalidRequest request) + { + //arrange + //act + var response = await Client.PostAsJsonAsync("/api/v1/users/register", request.Request); + + //assert + response.Should().Be400BadRequest(); + + var problemDetails = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); + problemDetails.ShouldContainValidationErrorFor(request.InvalidProperty); + } + +} diff --git a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccessTests.cs b/tests/TeamUp.EndToEndTests/EndpointTests/UserAccessTests.cs deleted file mode 100644 index 2b39ad0..0000000 --- a/tests/TeamUp.EndToEndTests/EndpointTests/UserAccessTests.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; - -using TeamUp.Application.Users; -using TeamUp.Contracts.Users; - -namespace TeamUp.EndToEndTests.EndpointTests; - -public sealed class UserAccessTests : BaseEndpointTests -{ - public UserAccessTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { } - - [Fact] - public async Task RegisterUser_Should_CreateNewUserInDatabase_And_SendActivationEmail() - { - //arrange - var request = UserGenerator.ValidRegisterUserRequest.Generate(); - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/register", request); - - //assert - response.Should().Be201Created(); - - var userId = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); - userId.ShouldNotBeNull(); - - var user = await UseDbContextAsync(dbContext => dbContext.Users.FindAsync(userId)); - user.ShouldNotBeNull(); - - user.Name.Should().BeEquivalentTo(request.Name); - user.Email.Should().BeEquivalentTo(request.Email); - user.Password.Should().NotBeEquivalentTo(request.Password); - - await WaitForIntegrationEventsAsync(); //wait for email - Inbox.Should().Contain(email => email.EmailAddress == request.Email); - } - - [Fact] - public async Task RegisterUser_WithAlreadyUsedEmail_Should_ReturnConflict() - { - //arrange - var user = UserGenerator.ActivatedUser.Generate(); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(user); - return dbContext.SaveChangesAsync(); - }); - - var request = new RegisterUserRequest() - { - Email = user.Email, - Name = F.Internet.UserName(), - Password = UserGenerator.GenerateValidPassword(), - }; - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/register", request); - - //assert - response.Should().Be409Conflict(); - - var problemDetails = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); - problemDetails.ShouldContainError(UserErrors.ConflictingEmail); - } - - [Theory] - [ClassData(typeof(UserGenerator.InvalidRegisterUserRequests))] - public async Task RegisterUser_WithInvalidProperties_Should_ReturnValidationErrors(InvalidRequest request) - { - //arrange - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/register", request.Request); - - //assert - response.Should().Be400BadRequest(); - - var problemDetails = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); - problemDetails.ShouldContainValidationErrorFor(request.InvalidProperty); - } - - [Fact] - public async Task Login_AsActivatedUser_Should_GenerateValidJwtToken() - { - //arrange - var passwordService = AppFactory.Services.GetRequiredService(); - - var rawPassword = UserGenerator.GenerateValidPassword(); - var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.Activated); - - await UseDbContextAsync(dbContext => - { - dbContext.Add(user); - return dbContext.SaveChangesAsync(); - }); - - var request = new LoginRequest - { - Email = user.Email, - Password = rawPassword - }; - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); - - //assert - response.Should().Be200Ok(); - - var token = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); - token.Should().NotBeNullOrEmpty(); - - var handler = new JwtSecurityTokenHandler(); - var jwt = handler.ReadJwtToken(token); - - jwt.ShouldNotBeNull(); - jwt.ValidTo.Ticks.Should().BeGreaterThan(DateTime.UtcNow.Ticks); - jwt.Claims.Select(claim => (claim.Type, claim.Value)) - .Should() - .Contain([ - (ClaimTypes.NameIdentifier, user.Id.Value.ToString()), - (ClaimTypes.Name, user.Name), - (ClaimTypes.Email, user.Email) - ]); - } - - [Fact] - public async Task Login_AsInactivatedUser_Should_ReturnUnauthorized() - { - //arrange - var passwordService = AppFactory.Services.GetRequiredService(); - - var rawPassword = UserGenerator.GenerateValidPassword(); - var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.NotActivated); - - await UseDbContextAsync(dbContext => - { - dbContext.Add(user); - return dbContext.SaveChangesAsync(); - }); - - var request = new LoginRequest - { - Email = user.Email, - Password = rawPassword - }; - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); - - //assert - response.Should().Be401Unauthorized(); - - var problemDetails = await response.Content.ReadFromJsonAsync(); - problemDetails.ShouldContainError(AuthenticationErrors.NotActivatedAccount); - } - - [Fact] - public async Task Login_AsNonexistentUser_Should_ReturnUnauthorized() - { - //arrange - - var request = new LoginRequest - { - Email = F.Internet.Email(), - Password = UserGenerator.GenerateValidPassword() - }; - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); - - //assert - response.Should().Be401Unauthorized(); - - var problemDetails = await response.Content.ReadFromJsonAsync(); - problemDetails.ShouldContainError(AuthenticationErrors.InvalidCredentials); - } - - [Fact] - public async Task Login_WithIncorrectPassword_Should_ReturnUnauthorized() - { - //arrange - var passwordService = AppFactory.Services.GetRequiredService(); - - var rawPassword = UserGenerator.GenerateValidPassword(); - var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.Activated); - - await UseDbContextAsync(dbContext => - { - dbContext.Add(user); - return dbContext.SaveChangesAsync(); - }); - - var request = new LoginRequest - { - Email = user.Email, - Password = rawPassword + "x" - }; - - //act - var response = await Client.PostAsJsonAsync("/api/v1/users/login", request); - - //assert - response.Should().Be401Unauthorized(); - - var problemDetails = await response.Content.ReadFromJsonAsync(); - problemDetails.ShouldContainError(AuthenticationErrors.InvalidCredentials); - } - - [Fact] - public async Task ActivateAccount_Should_SetUserStatusAsActivatedInDatabase() - { - //arrange - var passwordService = AppFactory.Services.GetRequiredService(); - - var rawPassword = UserGenerator.GenerateValidPassword(); - var user = UserGenerator.GenerateUser(passwordService.HashPassword(rawPassword), UserStatus.NotActivated); - - await UseDbContextAsync(dbContext => - { - dbContext.Add(user); - return dbContext.SaveChangesAsync(); - }); - - //act - var response = await Client.PostAsJsonAsync($"/api/v1/users/{user.Id.Value}/activate", EmptyObject); - - //assert - response.Should().Be200Ok(); - - var activatedUser = await UseDbContextAsync(dbContext => dbContext.Users.FindAsync(user.Id)); - activatedUser.ShouldNotBeNull(); - activatedUser.Status.Should().Be(UserStatus.Activated); - } - - [Fact] - public async Task GetMyProfile_WhenUnauthenticated_Should_ReturnUnauthorized() - { - //arrange - //act - var response = await Client.GetAsync("/api/v1/users/my-profile"); - - //assert - response.Should().Be401Unauthorized(); - } - - [Fact] - public async Task GetMyProfile_AsExistingUser_Should_ReturnUserDetails() - { - //arrange - var user = UserGenerator.ActivatedUser.Generate(); - - await UseDbContextAsync(dbContext => - { - dbContext.Users.Add(user); - return dbContext.SaveChangesAsync(); - }); - - Authenticate(user); - - //act - var response = await Client.GetAsync("/api/v1/users/my-profile"); - - //assert - response.Should().Be200Ok(); - - var userResponse = await response.Content.ReadFromJsonAsync(JsonSerializerOptions); - user.Should().BeEquivalentTo(userResponse, o => o.ExcludingMissingMembers()); - } -}