From d42b58c28250f6b7be623963de8733529f93d252 Mon Sep 17 00:00:00 2001 From: Ben Sampica Date: Thu, 3 Nov 2022 22:47:31 -0500 Subject: [PATCH 1/5] Allow fines to be attached to teams. --- src/Client/Features/Admin/AddPlayer.razor.cs | 2 +- src/Client/Features/Fines/List.razor | 7 +- src/Client/Features/Players/AddFine.razor | 2 +- src/Client/Features/Players/AddFine.razor.cs | 6 +- src/Client/Features/Teams/AddFine.razor | 12 + src/Client/Features/Teams/AddFine.razor.cs | 45 ++ src/Client/Features/Teams/Detail.razor | 7 + src/Client/Features/Teams/Detail.razor.cs | 10 + src/Server/DynamoLeagueBlazor.Server.csproj | 1 + src/Server/Features/Fines/List.cs | 4 +- src/Server/Features/Players/AddFine.cs | 24 +- src/Server/Features/Teams/AddFine.cs | 58 ++ .../20221104021126_AddFineToTeam.Designer.cs | 632 ++++++++++++++++++ .../20221104021126_AddFineToTeam.cs | 56 ++ .../ApplicationDbContextModelSnapshot.cs | 8 +- src/Server/Models/Fine.cs | 8 +- src/Server/Models/Player.cs | 2 +- src/Server/Models/Team.cs | 2 + src/Server/Program.cs | 5 +- src/Shared/Features/Fines/List.cs | 2 +- src/Shared/Features/Players/AddFine.cs | 6 +- src/Shared/Features/Teams/AddFine.cs | 23 + src/Tests/Features/Admin/StartSeasonTests.cs | 24 +- .../Features/Dashboard/TopOffendersTests.cs | 12 +- .../Features/Dashboard/TopTeamFinesTests.cs | 18 +- src/Tests/Features/Fines/ListTests.cs | 4 +- src/Tests/Features/Fines/ManageFineTests.cs | 8 +- src/Tests/Features/FreeAgents/AddBidTests.cs | 2 + src/Tests/Features/Players/AddFineTests.cs | 14 +- src/Tests/Features/Teams/AddFineTests.cs | 93 +++ 30 files changed, 1032 insertions(+), 65 deletions(-) create mode 100644 src/Client/Features/Teams/AddFine.razor create mode 100644 src/Client/Features/Teams/AddFine.razor.cs create mode 100644 src/Server/Features/Teams/AddFine.cs create mode 100644 src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.Designer.cs create mode 100644 src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.cs create mode 100644 src/Shared/Features/Teams/AddFine.cs create mode 100644 src/Tests/Features/Teams/AddFineTests.cs diff --git a/src/Client/Features/Admin/AddPlayer.razor.cs b/src/Client/Features/Admin/AddPlayer.razor.cs index a7894db..f95d4d7 100644 --- a/src/Client/Features/Admin/AddPlayer.razor.cs +++ b/src/Client/Features/Admin/AddPlayer.razor.cs @@ -15,7 +15,7 @@ public sealed partial class AddPlayer : IDisposable private TeamNameListResult _teamList = new(); private AddPlayerRequest _addPlayerForm = new(); private AddPlayerRequestValidator _validator = null!; - private PlayerPreviewRequest _playerPreviewForm = new(); + private readonly PlayerPreviewRequest _playerPreviewForm = new(); private string _previewHeadshotUrl = string.Empty; private bool _isPreviewButtonDisabled; private bool _isProcessingForm; diff --git a/src/Client/Features/Fines/List.razor b/src/Client/Features/Fines/List.razor index 2e6320e..0934694 100644 --- a/src/Client/Features/Fines/List.razor +++ b/src/Client/Features/Fines/List.razor @@ -20,7 +20,7 @@ Actions - Name + Name Team Reason Amount @@ -35,7 +35,10 @@ - + @if(fineItem.PlayerName != null) + { + + } diff --git a/src/Client/Features/Players/AddFine.razor b/src/Client/Features/Players/AddFine.razor index 50dd63b..49f22ff 100644 --- a/src/Client/Features/Players/AddFine.razor +++ b/src/Client/Features/Players/AddFine.razor @@ -2,7 +2,7 @@ { - + diff --git a/src/Client/Features/Players/AddFine.razor.cs b/src/Client/Features/Players/AddFine.razor.cs index 14a716c..871764f 100644 --- a/src/Client/Features/Players/AddFine.razor.cs +++ b/src/Client/Features/Players/AddFine.razor.cs @@ -9,14 +9,14 @@ public sealed partial class AddFine : IDisposable [CascadingParameter] MudDialogInstance MudDialogInstance { get; set; } = null!; [Parameter, EditorRequired] public int PlayerId { get; set; } - private AddFineRequest _form = null!; + private AddPlayerFineRequest _form = null!; private FineDetailResult? _fineDetail; private bool _processingForm; private readonly CancellationTokenSource _cts = new(); protected override async Task OnInitializedAsync() { - _form = new AddFineRequest { PlayerId = PlayerId }; + _form = new AddPlayerFineRequest { PlayerId = PlayerId }; await GetPlayerFineDetailsAsync(); } @@ -30,7 +30,7 @@ private async Task OnValidSubmitAsync() { _processingForm = true; - var response = await HttpClient.PostAsJsonAsync(AddFineRouteFactory.Uri, _form); + var response = await HttpClient.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, _form); if (response.IsSuccessStatusCode) { diff --git a/src/Client/Features/Teams/AddFine.razor b/src/Client/Features/Teams/AddFine.razor new file mode 100644 index 0000000..c9da928 --- /dev/null +++ b/src/Client/Features/Teams/AddFine.razor @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/Client/Features/Teams/AddFine.razor.cs b/src/Client/Features/Teams/AddFine.razor.cs new file mode 100644 index 0000000..4a55ca7 --- /dev/null +++ b/src/Client/Features/Teams/AddFine.razor.cs @@ -0,0 +1,45 @@ +using DynamoLeagueBlazor.Shared.Features.Teams; + +namespace DynamoLeagueBlazor.Client.Features.Teams; + +public sealed partial class AddFine : IDisposable +{ + [Inject] private HttpClient HttpClient { get; set; } = null!; + [Inject] private ISnackbar SnackBar { get; set; } = null!; + [CascadingParameter] MudDialogInstance MudDialogInstance { get; set; } = null!; + [Parameter, EditorRequired] public int TeamId { get; set; } + + private AddTeamFineRequest _form = null!; + private bool _processingForm; + private readonly CancellationTokenSource _cts = new(); + + protected override void OnInitialized() + { + _form = new AddTeamFineRequest { TeamId = TeamId }; + } + + private async Task OnValidSubmitAsync() + { + _processingForm = true; + + var response = await HttpClient.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, _form); + + if (response.IsSuccessStatusCode) + { + SnackBar.Add("Successfully added a fine.", Severity.Success); + } + else + { + SnackBar.Add("Something went wrong...", Severity.Error); + } + + _processingForm = false; + MudDialogInstance.Close(); + } + + public void Dispose() + { + _cts.Cancel(); + _cts.Dispose(); + } +} diff --git a/src/Client/Features/Teams/Detail.razor b/src/Client/Features/Teams/Detail.razor index 3d856a3..cf626e0 100644 --- a/src/Client/Features/Teams/Detail.razor +++ b/src/Client/Features/Teams/Detail.razor @@ -31,6 +31,13 @@ else @_result.Name Remaining Cap Space @_result.CapSpace + + + + Fine This Team + + + diff --git a/src/Client/Features/Teams/Detail.razor.cs b/src/Client/Features/Teams/Detail.razor.cs index a8212d0..e9745bb 100644 --- a/src/Client/Features/Teams/Detail.razor.cs +++ b/src/Client/Features/Teams/Detail.razor.cs @@ -101,6 +101,16 @@ private async Task OpenSignPlayerDialogAsync(int playerId) } } + private void OpenAddFineDialog(int playerId) + { + var parameters = new DialogParameters + { + { nameof(AddFine.TeamId), playerId } + }; + + DialogService.Show("Add A New Fine", parameters); + } + public void Dispose() { _cts.Cancel(); diff --git a/src/Server/DynamoLeagueBlazor.Server.csproj b/src/Server/DynamoLeagueBlazor.Server.csproj index 76bf823..ad8c56b 100644 --- a/src/Server/DynamoLeagueBlazor.Server.csproj +++ b/src/Server/DynamoLeagueBlazor.Server.csproj @@ -32,6 +32,7 @@ + diff --git a/src/Server/Features/Fines/List.cs b/src/Server/Features/Fines/List.cs index a32febc..eed8235 100644 --- a/src/Server/Features/Fines/List.cs +++ b/src/Server/Features/Fines/List.cs @@ -53,8 +53,8 @@ public ListMappingProfile() { CreateMap() .ForMember(d => d.Status, mo => mo.MapFrom(s => s.Status ? "Approved" : "Pending")) - .ForMember(d => d.PlayerName, mo => mo.MapFrom(s => s.Player.Name)) - .ForMember(d => d.PlayerHeadShotUrl, mo => mo.MapFrom(s => s.Player.HeadShotUrl)) + .ForMember(d => d.PlayerName, mo => mo.MapFrom(s => s.Player != null ? s.Player.Name : null)) + .ForMember(d => d.PlayerHeadShotUrl, mo => mo.MapFrom(s => s.Player != null ? s.Player.HeadShotUrl : null)) .ForMember(d => d.TeamName, mo => mo.MapFrom(s => s.Team.Name)) .ForMember(d => d.TeamLogoUrl, mo => mo.MapFrom(s => s.Team.LogoUrl)) .ForMember(d => d.Amount, mo => mo.MapFrom(s => s.Amount)); diff --git a/src/Server/Features/Players/AddFine.cs b/src/Server/Features/Players/AddFine.cs index 09fc4ea..7838ca3 100644 --- a/src/Server/Features/Players/AddFine.cs +++ b/src/Server/Features/Players/AddFine.cs @@ -3,40 +3,40 @@ namespace DynamoLeagueBlazor.Server.Features.Fines; -[Route(AddFineRouteFactory.Uri)] +[Route(AddPlayerFineRouteFactory.Uri)] [ApiController] -public class AddFineController : ControllerBase +public class AddPlayerFineController : ControllerBase { private readonly IMediator _mediator; private readonly IMapper _mapper; - public AddFineController(IMediator mediator, IMapper mapper) + public AddPlayerFineController(IMediator mediator, IMapper mapper) { _mediator = mediator; _mapper = mapper; } [HttpPost] - public async Task PostAsync([FromBody] AddFineRequest request, CancellationToken cancellationToken) + public async Task PostAsync([FromBody] AddPlayerFineRequest request, CancellationToken cancellationToken) { - var query = _mapper.Map(request); + var query = _mapper.Map(request); return await _mediator.Send(query, cancellationToken); } } -public record AddFineCommand(int PlayerId, string FineReason) : IRequest { } +public record AddPlayerFineCommand(int PlayerId, string FineReason) : IRequest { } -public class AddFineHandler : IRequestHandler +public class AddPlayerFineHandler : IRequestHandler { private readonly ApplicationDbContext _dbContext; - public AddFineHandler(ApplicationDbContext dbContext) + public AddPlayerFineHandler(ApplicationDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(AddFineCommand request, CancellationToken cancellationToken) + public async Task Handle(AddPlayerFineCommand request, CancellationToken cancellationToken) { var player = (await _dbContext.Players .AsTracking() @@ -52,10 +52,10 @@ public async Task Handle(AddFineCommand request, CancellationToken cancella } } -public class AddFineMappingProfile : Profile +public class AddPlayerFineMappingProfile : Profile { - public AddFineMappingProfile() + public AddPlayerFineMappingProfile() { - CreateMap(); + CreateMap(); } } \ No newline at end of file diff --git a/src/Server/Features/Teams/AddFine.cs b/src/Server/Features/Teams/AddFine.cs new file mode 100644 index 0000000..0cdeb5c --- /dev/null +++ b/src/Server/Features/Teams/AddFine.cs @@ -0,0 +1,58 @@ +using DynamoLeagueBlazor.Shared.Features.Teams; + +namespace DynamoLeagueBlazor.Server.Features.Teams; + +[Route(AddTeamFineRouteFactory.Uri)] +[ApiController] +public class AddTeamFineController : ControllerBase +{ + private readonly IMediator _mediator; + private readonly IMapper _mapper; + + public AddTeamFineController(IMediator mediator, IMapper mapper) + { + _mediator = mediator; + _mapper = mapper; + } + + [HttpPost] + public async Task PostAsync([FromBody] AddTeamFineRequest request, CancellationToken cancellationToken) + { + var query = _mapper.Map(request); + + await _mediator.Send(query, cancellationToken); + } +} + +public record AddTeamFineCommand(int TeamId, string FineReason, decimal Amount) : IRequest { } + +public class AddTeamFineHandler : IRequestHandler +{ + private readonly ApplicationDbContext _dbContext; + + public AddTeamFineHandler(ApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(AddTeamFineCommand request, CancellationToken cancellationToken) + { + var team = (await _dbContext.Teams + .AsTracking() + .SingleAsync(p => p.Id == request.TeamId, cancellationToken)); + + team.AddFine(request.Amount, request.FineReason); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return Unit.Value; + } +} + +public class AddTeamFineMappingProfile : Profile +{ + public AddTeamFineMappingProfile() + { + CreateMap(); + } +} diff --git a/src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.Designer.cs b/src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.Designer.cs new file mode 100644 index 0000000..933a58c --- /dev/null +++ b/src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.Designer.cs @@ -0,0 +1,632 @@ +// +using System; +using DynamoLeagueBlazor.Server.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace DynamoLeagueBlazor.Server.Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20221104021126_AddFineToTeam")] + partial class AddFineToTeam + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.DeviceFlowCodes", b => + { + b.Property("UserCode") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("datetime2"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("UserCode"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.ToTable("DeviceCodes", (string)null); + }); + + modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.Key", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Algorithm") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DataProtected") + .HasColumnType("bit"); + + b.Property("IsX509Certificate") + .HasColumnType("bit"); + + b.Property("Use") + .HasColumnType("nvarchar(450)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Use"); + + b.ToTable("Keys"); + }); + + modelBuilder.Entity("Duende.IdentityServer.EntityFramework.Entities.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Key"); + + b.HasIndex("ConsumedTime"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("PersistedGrants", (string)null); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("bit"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("TeamId"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Bid", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("int"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("PlayerId") + .HasColumnType("int"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.HasIndex("TeamId"); + + b.ToTable("Bids"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Fine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,0)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("PlayerId") + .HasColumnType("int"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("bit"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("PlayerId"); + + b.HasIndex("TeamId"); + + b.ToTable("Fines"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContractValue") + .HasColumnType("int"); + + b.Property("EndOfFreeAgency") + .HasColumnType("datetime2"); + + b.Property("HeadShotUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Position") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("YearAcquired") + .HasColumnType("int"); + + b.Property("YearContractExpires") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Players"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("LogoUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationUser", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Bid", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Models.Player", "Player") + .WithMany("Bids") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DynamoLeagueBlazor.Server.Models.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Fine", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Models.Player", "Player") + .WithMany("Fines") + .HasForeignKey("PlayerId"); + + b.HasOne("DynamoLeagueBlazor.Server.Models.Team", "Team") + .WithMany("Fines") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Player", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Models.Team", "Team") + .WithMany("Players") + .HasForeignKey("TeamId"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("DynamoLeagueBlazor.Server.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Player", b => + { + b.Navigation("Bids"); + + b.Navigation("Fines"); + }); + + modelBuilder.Entity("DynamoLeagueBlazor.Server.Models.Team", b => + { + b.Navigation("Fines"); + + b.Navigation("Players"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.cs b/src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.cs new file mode 100644 index 0000000..f1edc46 --- /dev/null +++ b/src/Server/Infrastructure/Migrations/20221104021126_AddFineToTeam.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DynamoLeagueBlazor.Server.Infrastructure.Migrations +{ + public partial class AddFineToTeam : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Fines_Players_PlayerId", + table: "Fines"); + + migrationBuilder.AlterColumn( + name: "PlayerId", + table: "Fines", + type: "int", + nullable: true, + oldClrType: typeof(int), + oldType: "int"); + + migrationBuilder.AddForeignKey( + name: "FK_Fines_Players_PlayerId", + table: "Fines", + column: "PlayerId", + principalTable: "Players", + principalColumn: "Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Fines_Players_PlayerId", + table: "Fines"); + + migrationBuilder.AlterColumn( + name: "PlayerId", + table: "Fines", + type: "int", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "int", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Fines_Players_PlayerId", + table: "Fines", + column: "PlayerId", + principalTable: "Players", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Server/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Server/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 5ec7d47..8d7ec19 100644 --- a/src/Server/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Server/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "6.0.5") + .HasAnnotation("ProductVersion", "6.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); @@ -306,7 +306,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedOn") .HasColumnType("datetime2"); - b.Property("PlayerId") + b.Property("PlayerId") .HasColumnType("int"); b.Property("Reason") @@ -538,9 +538,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("DynamoLeagueBlazor.Server.Models.Player", "Player") .WithMany("Fines") - .HasForeignKey("PlayerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("PlayerId"); b.HasOne("DynamoLeagueBlazor.Server.Models.Team", "Team") .WithMany("Fines") diff --git a/src/Server/Models/Fine.cs b/src/Server/Models/Fine.cs index 8196df9..6ee98aa 100644 --- a/src/Server/Models/Fine.cs +++ b/src/Server/Models/Fine.cs @@ -4,12 +4,12 @@ namespace DynamoLeagueBlazor.Server.Models; public record Fine : BaseEntity { - public Fine(decimal amount, string reason, int playerId, int teamId) + public Fine(decimal amount, string reason, int teamId, int? playerId = null) { Amount = amount; Reason = reason; - PlayerId = playerId; TeamId = teamId; + PlayerId = playerId; } [Column(TypeName = "decimal(18,0)")] @@ -17,9 +17,9 @@ public Fine(decimal amount, string reason, int playerId, int teamId) public bool Status { get; set; } public DateTime CreatedOn { get; set; } = DateTime.Now; public string Reason { get; set; } - public int PlayerId { get; set; } public int TeamId { get; set; } + public int? PlayerId { get; set; } - public Player Player { get; private set; } = null!; + public Player? Player { get; private set; } public Team Team { get; private set; } = null!; } diff --git a/src/Server/Models/Player.cs b/src/Server/Models/Player.cs index fbe0021..4624330 100644 --- a/src/Server/Models/Player.cs +++ b/src/Server/Models/Player.cs @@ -118,7 +118,7 @@ public Fine AddFine(decimal amount, string reason) if (TeamId is null) throw new InvalidOperationException("A player must first be assigned to a team to add a fine."); - var fine = new Fine(amount, reason, Id, TeamId!.Value); + var fine = new Fine(amount, reason, TeamId!.Value, Id); Fines.Add(fine); diff --git a/src/Server/Models/Team.cs b/src/Server/Models/Team.cs index 6a5c55c..77b857c 100644 --- a/src/Server/Models/Team.cs +++ b/src/Server/Models/Team.cs @@ -15,4 +15,6 @@ public Team(string name, string logoUrl) public ICollection Fines { get; private set; } = new HashSet(); public int CapSpace() => Players.Sum(p => p.ContractValue); + + public void AddFine(decimal amount, string reason) => Fines.Add(new Fine(amount, reason, Id)); } diff --git a/src/Server/Program.cs b/src/Server/Program.cs index f2356cd..bee77df 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -34,9 +34,11 @@ .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Duende", LogEventLevel.Error) .Enrich.FromLogContext() - .Enrich.WithProperty("Environment", builder.Environment.EnvironmentName) + .Enrich.WithEnvironmentName() .WriteTo.File("logs/log.log", rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Name} [{Level:u3}] {Message:lj}{NewLine}{Exception}"); + builder.Logging.ClearProviders(); + Log.Logger = loggerConfiguration.CreateLogger(); builder.Host.UseSerilog(); @@ -46,6 +48,7 @@ { options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + options.EnableSensitiveDataLogging(); }); builder.Services.AddDbContextFactory(lifetime: ServiceLifetime.Scoped); diff --git a/src/Shared/Features/Fines/List.cs b/src/Shared/Features/Fines/List.cs index 3fee1e9..47b2435 100644 --- a/src/Shared/Features/Fines/List.cs +++ b/src/Shared/Features/Fines/List.cs @@ -8,7 +8,7 @@ public class FineItem { public int Id { get; set; } public string PlayerHeadShotUrl { get; set; } = null!; - public string PlayerName { get; set; } = null!; + public string? PlayerName { get; set; } public string TeamName { get; set; } = null!; public string TeamLogoUrl { get; set; } = null!; public string Reason { get; set; } = null!; diff --git a/src/Shared/Features/Players/AddFine.cs b/src/Shared/Features/Players/AddFine.cs index a4ed63f..46a8ff6 100644 --- a/src/Shared/Features/Players/AddFine.cs +++ b/src/Shared/Features/Players/AddFine.cs @@ -2,13 +2,13 @@ namespace DynamoLeagueBlazor.Shared.Features.Players; -public class AddFineRequest +public class AddPlayerFineRequest { public int PlayerId { get; set; } public string FineReason { get; set; } = null!; } -public class AddFineRequestValidator : AbstractValidator +public class AddFineRequestValidator : AbstractValidator { public AddFineRequestValidator() { @@ -17,7 +17,7 @@ public AddFineRequestValidator() } } -public class AddFineRouteFactory +public class AddPlayerFineRouteFactory { public const string Uri = "api/players/addfine"; } diff --git a/src/Shared/Features/Teams/AddFine.cs b/src/Shared/Features/Teams/AddFine.cs new file mode 100644 index 0000000..e7fd471 --- /dev/null +++ b/src/Shared/Features/Teams/AddFine.cs @@ -0,0 +1,23 @@ +namespace DynamoLeagueBlazor.Shared.Features.Teams; + +public class AddTeamFineRequest +{ + public int TeamId { get; set; } + public string FineReason { get; set; } = null!; + public int Amount { get; set; } +} + +public class AddFineRequestValidator : AbstractValidator +{ + public AddFineRequestValidator() + { + RuleFor(r => r.TeamId).GreaterThan(0); + RuleFor(r => r.FineReason).NotEmpty(); + RuleFor(r => r.Amount).GreaterThan(0); + } +} + +public class AddTeamFineRouteFactory +{ + public const string Uri = "api/teams/addfine"; +} \ No newline at end of file diff --git a/src/Tests/Features/Admin/StartSeasonTests.cs b/src/Tests/Features/Admin/StartSeasonTests.cs index ef06041..d72b440 100644 --- a/src/Tests/Features/Admin/StartSeasonTests.cs +++ b/src/Tests/Features/Admin/StartSeasonTests.cs @@ -65,12 +65,14 @@ public async Task GivenAuthenticatedAdmin_WhenAFineExistsBeforeJanuary1stOfTheCu var stubTeam = CreateFakeTeam(); await AddAsync(stubTeam); - var stubPlayer = CreateFakePlayer(); - stubPlayer.State = PlayerState.Rostered; - stubPlayer.TeamId = stubTeam.Id; - var mockFine = stubPlayer.AddFine(int.MaxValue, RandomString); + var mockPlayer = CreateFakePlayer(); + mockPlayer.State = PlayerState.Rostered; + mockPlayer.TeamId = stubTeam.Id; + await AddAsync(mockPlayer); + + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); mockFine.CreatedOn = DateTime.MinValue; - await AddAsync(stubPlayer); + await UpdateAsync(mockPlayer); var client = application.CreateClient(); @@ -90,12 +92,14 @@ public async Task GivenAuthenticatedAdmin_WhenAFineExistsOnOrAfterJanuary1stOfTh var stubTeam = CreateFakeTeam(); await AddAsync(stubTeam); - var stubPlayer = CreateFakePlayer(); - stubPlayer.State = PlayerState.Rostered; - stubPlayer.TeamId = stubTeam.Id; - var mockFine = stubPlayer.AddFine(int.MaxValue, RandomString); + var mockPlayer = CreateFakePlayer(); + mockPlayer.State = PlayerState.Rostered; + mockPlayer.TeamId = stubTeam.Id; + await AddAsync(mockPlayer); + + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); mockFine.CreatedOn = new DateTime(DateTime.Today.Year, 1, 1); - await AddAsync(stubPlayer); + await UpdateAsync(mockPlayer); var client = application.CreateClient(); diff --git a/src/Tests/Features/Dashboard/TopOffendersTests.cs b/src/Tests/Features/Dashboard/TopOffendersTests.cs index c990219..19ebecf 100644 --- a/src/Tests/Features/Dashboard/TopOffendersTests.cs +++ b/src/Tests/Features/Dashboard/TopOffendersTests.cs @@ -46,9 +46,11 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsOnePlayerWithAFine_ThenRe var mockPlayer = CreateFakePlayer(); mockPlayer.TeamId = stubTeam.Id; + await AddAsync(mockPlayer); + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); mockFine.Status = true; - await AddAsync(mockPlayer); + await UpdateAsync(mockFine); var client = application.CreateClient(); @@ -75,16 +77,20 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsElevenPlayersWithApproved { var mockPlayer = CreateFakePlayer(); mockPlayer.TeamId = stubTeam.Id; + await AddAsync(mockPlayer); + var fine = mockPlayer.AddFine(int.MaxValue, RandomString); fine.Status = true; - await AddAsync(mockPlayer); + await UpdateAsync(mockPlayer); } var eleventhPlayerWithFine = CreateFakePlayer(); eleventhPlayerWithFine.TeamId = stubTeam.Id; + await AddAsync(eleventhPlayerWithFine); + var lowestFine = eleventhPlayerWithFine.AddFine(int.MinValue, RandomString); lowestFine.Status = true; - await AddAsync(eleventhPlayerWithFine); + await UpdateAsync(eleventhPlayerWithFine); var client = application.CreateClient(); diff --git a/src/Tests/Features/Dashboard/TopTeamFinesTests.cs b/src/Tests/Features/Dashboard/TopTeamFinesTests.cs index f4b1781..24b33c3 100644 --- a/src/Tests/Features/Dashboard/TopTeamFinesTests.cs +++ b/src/Tests/Features/Dashboard/TopTeamFinesTests.cs @@ -24,11 +24,13 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsOneTeamWithAFine_ThenRetu var mockTeam = CreateFakeTeam(); await AddAsync(mockTeam); - var stubPlayer = CreateFakePlayer(); - stubPlayer.TeamId = mockTeam.Id; - var mockFine = stubPlayer.AddFine(int.MaxValue, RandomString); + var mockPlayer = CreateFakePlayer(); + mockPlayer.TeamId = mockTeam.Id; + await AddAsync(mockPlayer); + + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); mockFine.Status = true; - await AddAsync(stubPlayer); + await UpdateAsync(mockPlayer); var client = application.CreateClient(); @@ -53,15 +55,19 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsMultipleTeamsWithApproved var mockPlayer1 = CreateFakePlayer(); mockPlayer1.TeamId = stubTeam.Id; + await AddAsync(mockPlayer1); + var fine = mockPlayer1.AddFine(int.MaxValue, RandomString); fine.Status = true; - await AddAsync(mockPlayer1); + await UpdateAsync(mockPlayer1); var mockPlayer2 = CreateFakePlayer(); mockPlayer2.TeamId = stubTeam.Id; + await AddAsync(mockPlayer2); + var lowestFine = mockPlayer2.AddFine(int.MinValue, RandomString); lowestFine.Status = true; - await AddAsync(mockPlayer2); + await UpdateAsync(mockPlayer2); var client = application.CreateClient(); diff --git a/src/Tests/Features/Fines/ListTests.cs b/src/Tests/Features/Fines/ListTests.cs index 0ab3a5f..41b764a 100644 --- a/src/Tests/Features/Fines/ListTests.cs +++ b/src/Tests/Features/Fines/ListTests.cs @@ -26,9 +26,11 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsOneFine_ThenReturnsOneFin var mockPlayer = CreateFakePlayer(); mockPlayer.TeamId = mockTeam.Id; - var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); await AddAsync(mockPlayer); + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); + await UpdateAsync(mockPlayer); + var client = application.CreateClient(); var result = await client.GetFromJsonAsync(FineListRouteFactory.Uri); diff --git a/src/Tests/Features/Fines/ManageFineTests.cs b/src/Tests/Features/Fines/ManageFineTests.cs index ab6e8d0..eb91fc5 100644 --- a/src/Tests/Features/Fines/ManageFineTests.cs +++ b/src/Tests/Features/Fines/ManageFineTests.cs @@ -48,9 +48,11 @@ public async Task GivenAuthenticatedAdmin_WhenFineIsApproved_ThenUpdatesIt() var mockPlayer = CreateFakePlayer(); mockPlayer.TeamId = stubTeam.Id; - var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); await AddAsync(mockPlayer); + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); + await UpdateAsync(mockPlayer); + var mockRequest = CreateFakeValidRequest(); mockRequest.Approved = true; mockRequest.FineId = mockFine.Id; @@ -76,9 +78,11 @@ public async Task GivenAuthenticatedAdmin_WhenFineIsNotApproved_ThenDeletesIt() var mockPlayer = CreateFakePlayer(); mockPlayer.TeamId = stubTeam.Id; - var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); await AddAsync(mockPlayer); + var mockFine = mockPlayer.AddFine(int.MaxValue, RandomString); + await UpdateAsync(mockPlayer); + var mockRequest = CreateFakeValidRequest(); mockRequest.Approved = false; mockRequest.FineId = mockFine.Id; diff --git a/src/Tests/Features/FreeAgents/AddBidTests.cs b/src/Tests/Features/FreeAgents/AddBidTests.cs index 9ab7770..22da670 100644 --- a/src/Tests/Features/FreeAgents/AddBidTests.cs +++ b/src/Tests/Features/FreeAgents/AddBidTests.cs @@ -155,6 +155,7 @@ public async Task GivenABidOfOne_WhenAPlayerAlreadyHasBidOfOneDollar_ThenIsNotVa var stubTeam = CreateFakeTeam(); await AddAsync(stubTeam); var mockPlayer = CreateFakePlayer(); + mockPlayer.TeamId = stubTeam.Id; await AddAsync(mockPlayer); mockPlayer.AddBid(1, stubTeam.Id); await UpdateAsync(mockPlayer); @@ -172,6 +173,7 @@ public async Task GivenABidOfTwo_WhenAPlayerAlreadyHasBidOfOneDollar_ThenIsValid var stubTeam = CreateFakeTeam(); await AddAsync(stubTeam); var mockPlayer = CreateFakePlayer(); + mockPlayer.TeamId = stubTeam.Id; await AddAsync(mockPlayer); mockPlayer.AddBid(1, stubTeam.Id); await UpdateAsync(mockPlayer); diff --git a/src/Tests/Features/Players/AddFineTests.cs b/src/Tests/Features/Players/AddFineTests.cs index 9d67f41..683b5c5 100644 --- a/src/Tests/Features/Players/AddFineTests.cs +++ b/src/Tests/Features/Players/AddFineTests.cs @@ -5,9 +5,9 @@ namespace DynamoLeagueBlazor.Tests.Features.Fines; public class AddFineTests : IntegrationTestBase { - private static AddFineRequest CreateFakeValidRequest() + private static AddPlayerFineRequest CreateFakeValidRequest() { - var faker = new AutoFaker() + var faker = new AutoFaker() .RuleFor(f => f.PlayerId, 1); return faker.Generate(); @@ -21,7 +21,7 @@ public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() var client = application.CreateClient(); var stubRequest = CreateFakeValidRequest(); - var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, stubRequest); + var response = await client.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, stubRequest); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -43,7 +43,7 @@ public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() var client = application.CreateClient(); - var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, stubRequest); + var response = await client.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, stubRequest); response.StatusCode.Should().Be(HttpStatusCode.OK); @@ -58,7 +58,7 @@ public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() public class AddFineRequestValidatorTests : IntegrationTestBase { - private AddFineRequestValidator _validator = null!; + private readonly AddFineRequestValidator _validator = null!; public AddFineRequestValidatorTests() { @@ -73,7 +73,7 @@ public AddFineRequestValidatorTests() [InlineData(1, "Test", true)] public void GivenDifferentRequests_ThenReturnsExpectedResult(int playerId, string reason, bool expectedResult) { - var request = new AddFineRequest { PlayerId = playerId, FineReason = reason }; + var request = new AddPlayerFineRequest { PlayerId = playerId, FineReason = reason }; var result = _validator.Validate(request); @@ -83,7 +83,7 @@ public void GivenDifferentRequests_ThenReturnsExpectedResult(int playerId, strin public class FineDetailRequestValidatorTests : IntegrationTestBase { - private FineDetailRequestValidator _validator = null!; + private readonly FineDetailRequestValidator _validator = null!; public FineDetailRequestValidatorTests() { diff --git a/src/Tests/Features/Teams/AddFineTests.cs b/src/Tests/Features/Teams/AddFineTests.cs new file mode 100644 index 0000000..16b1435 --- /dev/null +++ b/src/Tests/Features/Teams/AddFineTests.cs @@ -0,0 +1,93 @@ +using DynamoLeagueBlazor.Shared.Features.Teams; + +namespace DynamoLeagueBlazor.Tests.Features.Teams; + +public class AddFineTests : IntegrationTestBase +{ + private static AddTeamFineRequest CreateFakeValidRequest() + { + var faker = new AutoFaker() + .RuleFor(f => f.TeamId, 1) + .RuleFor(f => f.Amount, int.MaxValue); + + return faker.Generate(); + } + + [Fact] + public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() + { + var application = GetUnauthenticatedApplication(); + + var client = application.CreateClient(); + + var stubRequest = CreateFakeValidRequest(); + var response = await client.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, stubRequest); + + response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); + } + + [Fact] + public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() + { + var application = GetUserAuthenticatedApplication(); + + var stubTeam = CreateFakeTeam(); + await AddAsync(stubTeam); + + var mockRequest = CreateFakeValidRequest(); + mockRequest.TeamId = stubTeam.Id; + + var client = application.CreateClient(); + + var response = await client.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, mockRequest); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + + var fine = await FirstOrDefaultAsync(); + fine.Should().NotBeNull(); + fine!.TeamId.Should().Be(mockRequest.TeamId); + fine.Status.Should().BeFalse(); + fine.Reason.Should().Be(mockRequest.FineReason); + fine.Amount.Should().Be(mockRequest.Amount); + } +} +#pragma warning disable xUnit1012 // Null should not be used for value type parameters +public class AddFineRequestValidatorTests : IntegrationTestBase +{ + private readonly AddFineRequestValidator _validator = null!; + + public AddFineRequestValidatorTests() + { + _validator = GetRequiredService(); + + } + + [Theory] + [InlineData(null), InlineData(-1), InlineData(0)] + public void GivenInvalidTeamIds_ThenAreNotValid(int teamId) + => _validator.TestValidate(new AddTeamFineRequest { TeamId = teamId }).ShouldHaveValidationErrorFor(v => v.TeamId); + + [Fact] + public void GivenAValidTeamId_ThenIsValid() + => _validator.TestValidate(new AddTeamFineRequest { TeamId = 1 }).ShouldNotHaveValidationErrorFor(v => v.TeamId); + + [Theory] + [InlineData(null), InlineData("")] + public void GivenInvalidFineReasons_ThenIsNotValid(string fineReason) + => _validator.TestValidate(new AddTeamFineRequest { FineReason = fineReason }).ShouldHaveValidationErrorFor(v => v.FineReason); + + [Fact] + public void GivenAValidFineReason_ThenIsValid() + => _validator.TestValidate(new AddTeamFineRequest { FineReason = RandomString }).ShouldNotHaveValidationErrorFor(v => v.FineReason); + + [Theory] + + [InlineData(null), InlineData(-1), InlineData(0)] + public void GivenInvalidAmounts_ThenAreNotValid(int amount) + => _validator.TestValidate(new AddTeamFineRequest { Amount = amount }).ShouldHaveValidationErrorFor(v => v.Amount); + + [Fact] + public void GivenAValidAmount_ThenIsValid() + => _validator.TestValidate(new AddTeamFineRequest { Amount = 1 }).ShouldNotHaveValidationErrorFor(v => v.Amount); +} +#pragma warning restore xUnit1012 // Null should not be used for value type parameters \ No newline at end of file From b9d332ae55fe574ca905feb421b159a76e8c4887 Mon Sep 17 00:00:00 2001 From: Ben Sampica Date: Thu, 3 Nov 2022 22:50:49 -0500 Subject: [PATCH 2/5] Add server side tests --- src/Server/Models/Team.cs | 9 ++++++++- src/Tests/Features/Fines/ListTests.cs | 25 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Server/Models/Team.cs b/src/Server/Models/Team.cs index 77b857c..1cef078 100644 --- a/src/Server/Models/Team.cs +++ b/src/Server/Models/Team.cs @@ -16,5 +16,12 @@ public Team(string name, string logoUrl) public int CapSpace() => Players.Sum(p => p.ContractValue); - public void AddFine(decimal amount, string reason) => Fines.Add(new Fine(amount, reason, Id)); + public Fine AddFine(decimal amount, string reason) + { + var fine = new Fine(amount, reason, Id); + + Fines.Add(fine); + + return fine; + } } diff --git a/src/Tests/Features/Fines/ListTests.cs b/src/Tests/Features/Fines/ListTests.cs index 41b764a..b40f6d4 100644 --- a/src/Tests/Features/Fines/ListTests.cs +++ b/src/Tests/Features/Fines/ListTests.cs @@ -17,7 +17,7 @@ public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() } [Fact] - public async Task GivenAnyAuthenticatedUser_WhenThereIsOneFine_ThenReturnsOneFine() + public async Task GivenAnyAuthenticatedUser_WhenThereIsOnePlayerFine_ThenReturnsOnePlayerFine() { var application = GetUserAuthenticatedApplication(); @@ -48,4 +48,27 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsOneFine_ThenReturnsOneFin fine.TeamName.Should().Be(mockTeam.Name); fine.TeamLogoUrl.Should().Be(mockTeam.LogoUrl); } + + [Fact] + public async Task GivenAnyAuthenticatedUser_WhenThereIsOneTeamFine_ThenReturnsOneTeamFine() + { + var application = GetUserAuthenticatedApplication(); + + var mockTeam = CreateFakeTeam(); + await AddAsync(mockTeam); + + mockTeam.AddFine(int.MaxValue, RandomString); + await UpdateAsync(mockTeam); + + var client = application.CreateClient(); + + var result = await client.GetFromJsonAsync(FineListRouteFactory.Uri); + + result.Should().NotBeNull(); + result!.Fines.Should().HaveCount(1); + + var fine = result!.Fines.First(); + fine.PlayerHeadShotUrl.Should().Be(null); + fine.PlayerName.Should().Be(null); + } } From 05a809f7927bd417ff3524c4e480aee849d00355 Mon Sep 17 00:00:00 2001 From: Ben Sampica Date: Thu, 3 Nov 2022 23:00:51 -0500 Subject: [PATCH 3/5] broken test --- src/Tests/Features/Teams/DetailTests.cs | 17 +++++++++++++++++ src/Tests/UITestBase.cs | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/src/Tests/Features/Teams/DetailTests.cs b/src/Tests/Features/Teams/DetailTests.cs index 81ba61f..ad0931d 100644 --- a/src/Tests/Features/Teams/DetailTests.cs +++ b/src/Tests/Features/Teams/DetailTests.cs @@ -138,4 +138,21 @@ public void GivenAUserHasATeamOfOne_WhenViewingThePageOfTeamTwo_ThenDoesNotShowA cut.Markup.Should().NotContain("Actions"); } + + [Fact] + public void GivenAnAdmin_ThenCanAddAFine() + { + var teamId = int.MaxValue; + AuthorizeAsAdmin(teamId); + + GetHttpHandler.When(HttpMethod.Get, TeamDetailRouteFactory.Create(teamId)) + .RespondsWithJson(AutoFaker.Generate()); + + var cut = RenderComponent(parameters => + { + parameters.Add(p => p.TeamId, teamId); + }); + + cut.Markup.Should().Contain("Fine This Team"); + } } \ No newline at end of file diff --git a/src/Tests/UITestBase.cs b/src/Tests/UITestBase.cs index de54523..56d99fa 100644 --- a/src/Tests/UITestBase.cs +++ b/src/Tests/UITestBase.cs @@ -58,6 +58,13 @@ public void AuthorizeAsUser(int teamId, bool adminApproved = true) authorizedState.SetPolicies(PolicyRequirements.IsAdminApproved); } } + + public void AuthorizeAsAdmin(int teamId, bool adminApproved = true) + { + AuthorizeAsUser(teamId, adminApproved); + + _testAuthorizationContext.Roles.Append(RoleName.Admin); + } } public static class UITestExtensions From b77fb0f58013e7736fbfedec37686e9ed9349a40 Mon Sep 17 00:00:00 2001 From: Ben Sampica Date: Sun, 6 Nov 2022 21:29:46 -0600 Subject: [PATCH 4/5] Add more tests and fix it all. --- src/Client/Features/Players/AddFine.razor | 4 +- src/Client/Features/Players/AddFine.razor.cs | 2 +- src/Client/Features/Teams/AddFine.razor | 4 +- src/Client/Features/Teams/AddFine.razor.cs | 2 +- src/Client/Features/Teams/Detail.razor | 2 +- src/Server/Features/Players/AddFine.cs | 2 +- src/Server/Features/Teams/AddFine.cs | 4 +- src/Server/Program.cs | 2 +- src/Shared/Features/Players/AddFine.cs | 4 +- src/Shared/Features/Teams/AddFine.cs | 4 +- src/Tests/FakeFactory.cs | 2 + .../Features/FreeAgents/BidCountdownTests.cs | 8 +-- src/Tests/Features/Players/AddFineTests.cs | 60 ++++++++++++++-- src/Tests/Features/Teams/AddFineTests.cs | 68 +++++++++++++++++-- src/Tests/Features/Teams/DetailTests.cs | 4 +- src/Tests/Features/Teams/SignPlayerTests.cs | 8 +-- src/Tests/UITestBase.cs | 55 +++++++++------ 17 files changed, 181 insertions(+), 54 deletions(-) diff --git a/src/Client/Features/Players/AddFine.razor b/src/Client/Features/Players/AddFine.razor index 49f22ff..c2159ba 100644 --- a/src/Client/Features/Players/AddFine.razor +++ b/src/Client/Features/Players/AddFine.razor @@ -1,6 +1,6 @@ @if(_fineDetail is null) { - + @@ -17,7 +17,7 @@ else This player's contract is worth @_fineDetail.ContractValue and when penalized their fine amount is @_fineDetail.FineAmount. - + diff --git a/src/Client/Features/Players/AddFine.razor.cs b/src/Client/Features/Players/AddFine.razor.cs index 871764f..c7d34d2 100644 --- a/src/Client/Features/Players/AddFine.razor.cs +++ b/src/Client/Features/Players/AddFine.razor.cs @@ -30,7 +30,7 @@ private async Task OnValidSubmitAsync() { _processingForm = true; - var response = await HttpClient.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, _form); + var response = await HttpClient.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, _form, _cts.Token); if (response.IsSuccessStatusCode) { diff --git a/src/Client/Features/Teams/AddFine.razor b/src/Client/Features/Teams/AddFine.razor index c9da928..f8efd4a 100644 --- a/src/Client/Features/Teams/AddFine.razor +++ b/src/Client/Features/Teams/AddFine.razor @@ -2,8 +2,8 @@ - - + + diff --git a/src/Client/Features/Teams/AddFine.razor.cs b/src/Client/Features/Teams/AddFine.razor.cs index 4a55ca7..a8917f6 100644 --- a/src/Client/Features/Teams/AddFine.razor.cs +++ b/src/Client/Features/Teams/AddFine.razor.cs @@ -22,7 +22,7 @@ private async Task OnValidSubmitAsync() { _processingForm = true; - var response = await HttpClient.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, _form); + var response = await HttpClient.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, _form, _cts.Token); if (response.IsSuccessStatusCode) { diff --git a/src/Client/Features/Teams/Detail.razor b/src/Client/Features/Teams/Detail.razor index cf626e0..a5e6bc0 100644 --- a/src/Client/Features/Teams/Detail.razor +++ b/src/Client/Features/Teams/Detail.razor @@ -31,7 +31,7 @@ else @_result.Name Remaining Cap Space @_result.CapSpace - + Fine This Team diff --git a/src/Server/Features/Players/AddFine.cs b/src/Server/Features/Players/AddFine.cs index 7838ca3..25657cf 100644 --- a/src/Server/Features/Players/AddFine.cs +++ b/src/Server/Features/Players/AddFine.cs @@ -3,8 +3,8 @@ namespace DynamoLeagueBlazor.Server.Features.Fines; -[Route(AddPlayerFineRouteFactory.Uri)] [ApiController] +[Route(AddPlayerFineRouteFactory.Uri)] public class AddPlayerFineController : ControllerBase { private readonly IMediator _mediator; diff --git a/src/Server/Features/Teams/AddFine.cs b/src/Server/Features/Teams/AddFine.cs index 0cdeb5c..87fbb1d 100644 --- a/src/Server/Features/Teams/AddFine.cs +++ b/src/Server/Features/Teams/AddFine.cs @@ -1,9 +1,11 @@ using DynamoLeagueBlazor.Shared.Features.Teams; +using DynamoLeagueBlazor.Shared.Infastructure.Identity; namespace DynamoLeagueBlazor.Server.Features.Teams; -[Route(AddTeamFineRouteFactory.Uri)] +[Authorize(Policy = PolicyRequirements.Admin)] [ApiController] +[Route(AddTeamFineRouteFactory.Uri)] public class AddTeamFineController : ControllerBase { private readonly IMediator _mediator; diff --git a/src/Server/Program.cs b/src/Server/Program.cs index bee77df..a04d141 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -86,7 +86,7 @@ builder.Services.AddMediatR(Assembly.GetExecutingAssembly()); builder.Services.AddFluentValidation(fv => { - fv.RegisterValidatorsFromAssemblyContaining(); + fv.RegisterValidatorsFromAssemblyContaining(); }); builder.Services.AddTransient(); builder.Services.AddTransient(); diff --git a/src/Shared/Features/Players/AddFine.cs b/src/Shared/Features/Players/AddFine.cs index 46a8ff6..7aa5b9c 100644 --- a/src/Shared/Features/Players/AddFine.cs +++ b/src/Shared/Features/Players/AddFine.cs @@ -8,9 +8,9 @@ public class AddPlayerFineRequest public string FineReason { get; set; } = null!; } -public class AddFineRequestValidator : AbstractValidator +public class AddPlayerFineRequestValidator : AbstractValidator { - public AddFineRequestValidator() + public AddPlayerFineRequestValidator() { RuleFor(r => r.PlayerId).GreaterThan(0); RuleFor(r => r.FineReason).NotEmpty(); diff --git a/src/Shared/Features/Teams/AddFine.cs b/src/Shared/Features/Teams/AddFine.cs index e7fd471..b2f6cc8 100644 --- a/src/Shared/Features/Teams/AddFine.cs +++ b/src/Shared/Features/Teams/AddFine.cs @@ -7,9 +7,9 @@ public class AddTeamFineRequest public int Amount { get; set; } } -public class AddFineRequestValidator : AbstractValidator +public class AddTeamFineRequestValidator : AbstractValidator { - public AddFineRequestValidator() + public AddTeamFineRequestValidator() { RuleFor(r => r.TeamId).GreaterThan(0); RuleFor(r => r.FineReason).NotEmpty(); diff --git a/src/Tests/FakeFactory.cs b/src/Tests/FakeFactory.cs index b95660a..abef8e8 100644 --- a/src/Tests/FakeFactory.cs +++ b/src/Tests/FakeFactory.cs @@ -1,4 +1,5 @@ using DynamoLeagueBlazor.Server.Infrastructure.Identity; +using MudBlazor.Extensions; using Position = DynamoLeagueBlazor.Shared.Enums.Position; namespace DynamoLeagueBlazor.Tests; @@ -44,6 +45,7 @@ public static ApplicationUser CreateFakeUser(int teamId) public static FakePosition CreateFakePosition() => new(); public static string RandomString => AutoFaker.Generate(); + public static int RandomPositiveNumber => AutoFaker.Generate().EnsureRange(1, int.MaxValue); } public class FakePosition : Position diff --git a/src/Tests/Features/FreeAgents/BidCountdownTests.cs b/src/Tests/Features/FreeAgents/BidCountdownTests.cs index ef8f310..292f9be 100644 --- a/src/Tests/Features/FreeAgents/BidCountdownTests.cs +++ b/src/Tests/Features/FreeAgents/BidCountdownTests.cs @@ -9,14 +9,10 @@ public void CountsDownEverySecondUntilZero() { var cut = RenderComponent(parameters => { - parameters.Add(p => p.DateTime, DateTime.Now.AddSeconds(4)); + parameters.Add(p => p.DateTime, DateTime.Now.AddSeconds(2)); }); - cut.WaitForState(() => cut.Markup.Contains("1 second"), TimeSpan.FromSeconds(5)); - cut.WaitForState(() => cut.Markup.Contains("0 seconds"), TimeSpan.FromSeconds(3)); - - cut.Render(); - cut.Markup.Should().Contain("0 seconds"); + cut.WaitForState(() => cut.Markup.Contains("0 seconds"), TimeSpan.FromSeconds(5)); } [Fact] diff --git a/src/Tests/Features/Players/AddFineTests.cs b/src/Tests/Features/Players/AddFineTests.cs index 683b5c5..e1ec481 100644 --- a/src/Tests/Features/Players/AddFineTests.cs +++ b/src/Tests/Features/Players/AddFineTests.cs @@ -1,9 +1,10 @@ -using DynamoLeagueBlazor.Shared.Features.Players; +using DynamoLeagueBlazor.Client.Features.Players; +using DynamoLeagueBlazor.Shared.Features.Players; using DynamoLeagueBlazor.Shared.Utilities; namespace DynamoLeagueBlazor.Tests.Features.Fines; -public class AddFineTests : IntegrationTestBase +public class AddFineServerTests : IntegrationTestBase { private static AddPlayerFineRequest CreateFakeValidRequest() { @@ -56,13 +57,64 @@ public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() } } +public class AddFineUITests : UITestBase +{ + [Fact] + public async Task GivenAnInvalidForm_ThenDoesNotSubmit() + { + var result = new FineDetailResult { PlayerId = int.MaxValue, ContractValue = RandomString, FineAmount = RandomString }; + GetHttpHandler.When(HttpMethod.Get) + .RespondsWithJson(result) + .Verifiable(); + + GetHttpHandler.When(HttpMethod.Post, AddPlayerFineRouteFactory.Uri) + .Verifiable(); + + var cut = await RenderMudDialogAsync(); + + var submitButton = cut.Find("button"); + submitButton.Click(); + + GetHttpHandler.Verify((request) => request.Method(HttpMethod.Get.Method), IsSent.Once); + GetHttpHandler.VerifyNoOtherRequests(); + } + + [Fact] + public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() + { + var result = new FineDetailResult { PlayerId = int.MaxValue, ContractValue = RandomString, FineAmount = RandomString }; + GetHttpHandler.When(HttpMethod.Get) + .RespondsWithJson(result) + .Verifiable(); + + GetHttpHandler.When(HttpMethod.Post, AddPlayerFineRouteFactory.Uri) + .Respond(message => Task.FromResult(message.CreateResponse(HttpStatusCode.OK))) + .Verifiable(); + + var cut = await RenderMudDialogAsync(new DialogParameters + { + { nameof(AddFine.PlayerId), int.MaxValue } + }); + + // Fill the form and click submit. + var fineReason = cut.Find($"#{nameof(AddPlayerFineRequest.FineReason)}"); + fineReason.Change(RandomString); + + var submitButton = cut.Find("button"); + submitButton.Click(); + + MockSnackbar.Verify(s => s.Add(It.IsAny(), Severity.Success, It.IsAny>())); + GetHttpHandler.Verify(); + } +} + public class AddFineRequestValidatorTests : IntegrationTestBase { - private readonly AddFineRequestValidator _validator = null!; + private readonly AddPlayerFineRequestValidator _validator = null!; public AddFineRequestValidatorTests() { - _validator = GetRequiredService(); + _validator = GetRequiredService(); } diff --git a/src/Tests/Features/Teams/AddFineTests.cs b/src/Tests/Features/Teams/AddFineTests.cs index 16b1435..8a6c690 100644 --- a/src/Tests/Features/Teams/AddFineTests.cs +++ b/src/Tests/Features/Teams/AddFineTests.cs @@ -1,8 +1,9 @@ -using DynamoLeagueBlazor.Shared.Features.Teams; +using DynamoLeagueBlazor.Client.Features.Teams; +using DynamoLeagueBlazor.Shared.Features.Teams; namespace DynamoLeagueBlazor.Tests.Features.Teams; -public class AddFineTests : IntegrationTestBase +public class AddFineServerTests : IntegrationTestBase { private static AddTeamFineRequest CreateFakeValidRequest() { @@ -27,10 +28,23 @@ public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() } [Fact] - public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() + public async Task GivenAnAuthenticatedUser_ThenDoesNotAllowAccess() { var application = GetUserAuthenticatedApplication(); + var client = application.CreateClient(); + + var stubRequest = CreateFakeValidRequest(); + var response = await client.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, stubRequest); + + response.StatusCode.Should().Be(HttpStatusCode.Forbidden); + } + + [Fact] + public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() + { + var application = GetAdminAuthenticatedApplication(); + var stubTeam = CreateFakeTeam(); await AddAsync(stubTeam); @@ -51,14 +65,58 @@ public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() fine.Amount.Should().Be(mockRequest.Amount); } } + +public class AddFineUITests : UITestBase +{ + [Fact] + public async Task GivenAnInvalidForm_ThenDoesNotSubmit() + { + GetHttpHandler.When(HttpMethod.Post, AddTeamFineRouteFactory.Uri) + .Verifiable(); + + var cut = await RenderMudDialogAsync(); + + var submitButton = cut.Find("button"); + submitButton.Click(); + + GetHttpHandler.VerifyNoOtherRequests(); + } + + [Fact] + public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() + { + GetHttpHandler.When(HttpMethod.Post, AddTeamFineRouteFactory.Uri) + .Respond(message => Task.FromResult(message.CreateResponse(HttpStatusCode.OK))) + .Verifiable(); + + var cut = await RenderMudDialogAsync(new DialogParameters + { + { nameof(AddFine.TeamId), int.MaxValue } + }); + + // Fill the form and click submit. + var amount = cut.Find($"#{nameof(AddTeamFineRequest.Amount)}"); + amount.Change(RandomPositiveNumber); + + var fineReason = cut.Find($"#{nameof(AddTeamFineRequest.FineReason)}"); + fineReason.Change(RandomString); + + var submitButton = cut.Find("button"); + submitButton.Click(); + + MockSnackbar.Verify(s => s.Add(It.IsAny(), Severity.Success, It.IsAny>())); + GetHttpHandler.Verify(); + } +} + #pragma warning disable xUnit1012 // Null should not be used for value type parameters public class AddFineRequestValidatorTests : IntegrationTestBase { - private readonly AddFineRequestValidator _validator = null!; + private readonly AddTeamFineRequestValidator _validator = null!; public AddFineRequestValidatorTests() { - _validator = GetRequiredService(); + _validator = GetRequiredService(); } diff --git a/src/Tests/Features/Teams/DetailTests.cs b/src/Tests/Features/Teams/DetailTests.cs index ad0931d..bf8312f 100644 --- a/src/Tests/Features/Teams/DetailTests.cs +++ b/src/Tests/Features/Teams/DetailTests.cs @@ -4,7 +4,7 @@ namespace DynamoLeagueBlazor.Tests.Features.Teams; -public class DetailTests : IntegrationTestBase +public class DetailServerTests : IntegrationTestBase { [Fact] public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() @@ -84,7 +84,7 @@ public async Task GivenAnyAuthenticatedUser_WhenGivenValidTeamId_ThenReturnsExpe } } -public class DetailClientTests : UITestBase +public class DetailUITests : UITestBase { [Fact] public void WhenPageIsLoading_ThenShowsLoading() diff --git a/src/Tests/Features/Teams/SignPlayerTests.cs b/src/Tests/Features/Teams/SignPlayerTests.cs index d9ce076..b6ea104 100644 --- a/src/Tests/Features/Teams/SignPlayerTests.cs +++ b/src/Tests/Features/Teams/SignPlayerTests.cs @@ -60,9 +60,9 @@ public void WhenLoading_ThenShowsSkeleton() // Delay the response. GetHttpHandler.When(HttpMethod.Get) .TimesOutAfter(5000); - var component = RenderComponent(); + var cut = RenderComponent(); - component.HasComponent().Should().BeTrue(); + cut.HasComponent().Should().BeTrue(); } [Fact] @@ -73,10 +73,10 @@ public void WhenLoads_ThenCallsForContractOptions_AndShowsTheForm() .RespondsWithJson(contractOptions) .Verifiable(); - var component = RenderComponent(); + var cut = RenderComponent(); GetHttpHandler.Verify(); - component.Markup.Contains("form"); + cut.Markup.Contains("form"); } } diff --git a/src/Tests/UITestBase.cs b/src/Tests/UITestBase.cs index 56d99fa..2477915 100644 --- a/src/Tests/UITestBase.cs +++ b/src/Tests/UITestBase.cs @@ -1,6 +1,7 @@ using Bunit.TestDoubles; using DynamoLeagueBlazor.Client.Shared.Components; using DynamoLeagueBlazor.Shared.Infastructure.Identity; +using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; using MockHttp.Json; using MockHttp.Json.Newtonsoft; @@ -13,39 +14,50 @@ namespace DynamoLeagueBlazor.Tests; public class UITestBase : TestContextWrapper, IDisposable { - private readonly MockHttpHandler _mockHttpHandler = null!; - + private readonly MockHttpHandler _mockHttpHandler = new(); private readonly TestAuthorizationContext _testAuthorizationContext = null!; - protected Mock MockSnackbar = null!; + + protected Mock MockSnackbar = new(); public UITestBase() { var testContext = new TestContext(); - var mockSnackBar = new Mock(); - testContext.Services.AddMudServices(); - testContext.Services.AddSingleton(mockSnackBar.Object); testContext.Services.AddSingleton(Mock.Of()); testContext.JSInterop.SetupVoid(); + TestContext = testContext; + + _testAuthorizationContext = testContext.AddTestAuthorization(); + + testContext.Services.AddSingleton(MockSnackbar.Object); - var mockHttpHandler = new MockHttpHandler(); - var httpClient = new HttpClient(mockHttpHandler) + var httpClient = new HttpClient(_mockHttpHandler) { BaseAddress = new Uri("http://localhost") }; - testContext.Services.AddSingleton(httpClient); - _mockHttpHandler = mockHttpHandler; - - _testAuthorizationContext = testContext.AddTestAuthorization(); - TestContext = testContext; - MockSnackbar = mockSnackBar; } - public void Dispose() => TestContext?.Dispose(); - public MockHttpHandler GetHttpHandler => _mockHttpHandler; + public async Task> RenderMudDialogAsync(DialogParameters? dialogParameters = null) + where TComponent : ComponentBase + { + var mudDialogProvider = RenderComponent(); + var dialogService = TestContext!.Services.GetRequiredService(); + + if (dialogParameters is null) + { + await mudDialogProvider.InvokeAsync(() => dialogService.Show()); + } + else + { + await mudDialogProvider.InvokeAsync(() => dialogService.Show(string.Empty, dialogParameters)); + } + + return mudDialogProvider; + } + public void AuthorizeAsUser(int teamId, bool adminApproved = true) { var authorizedState = _testAuthorizationContext.SetAuthorized(RandomString); @@ -59,12 +71,17 @@ public void AuthorizeAsUser(int teamId, bool adminApproved = true) } } - public void AuthorizeAsAdmin(int teamId, bool adminApproved = true) + public void AuthorizeAsAdmin(int teamId) { - AuthorizeAsUser(teamId, adminApproved); + var authorizedState = _testAuthorizationContext.SetAuthorized(RandomString); + authorizedState.SetClaims( + new Claim(nameof(IUser.Approved), bool.TrueString), + new Claim(nameof(IUser.TeamId), teamId.ToString())); - _testAuthorizationContext.Roles.Append(RoleName.Admin); + _testAuthorizationContext.SetPolicies(PolicyRequirements.Admin, PolicyRequirements.IsAdminApproved); } + + public void Dispose() => TestContext?.Dispose(); } public static class UITestExtensions From 87be0714b3446c39d088fd01418724ae911c3fe6 Mon Sep 17 00:00:00 2001 From: Ben Sampica Date: Sun, 6 Nov 2022 21:32:55 -0600 Subject: [PATCH 5/5] rename --- src/Client/Features/Players/AddFine.razor.cs | 6 ++-- src/Client/Features/Teams/AddFine.razor.cs | 6 ++-- src/Server/Features/Players/AddFine.cs | 24 +++++++------- src/Server/Features/Teams/AddFine.cs | 24 +++++++------- src/Server/Program.cs | 2 +- src/Shared/Features/Players/AddFine.cs | 8 ++--- src/Shared/Features/Teams/AddFine.cs | 8 ++--- src/Tests/Features/Players/AddFineTests.cs | 20 ++++++------ src/Tests/Features/Teams/AddFineTests.cs | 34 ++++++++++---------- 9 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/Client/Features/Players/AddFine.razor.cs b/src/Client/Features/Players/AddFine.razor.cs index c7d34d2..0737f80 100644 --- a/src/Client/Features/Players/AddFine.razor.cs +++ b/src/Client/Features/Players/AddFine.razor.cs @@ -9,14 +9,14 @@ public sealed partial class AddFine : IDisposable [CascadingParameter] MudDialogInstance MudDialogInstance { get; set; } = null!; [Parameter, EditorRequired] public int PlayerId { get; set; } - private AddPlayerFineRequest _form = null!; + private AddFineRequest _form = null!; private FineDetailResult? _fineDetail; private bool _processingForm; private readonly CancellationTokenSource _cts = new(); protected override async Task OnInitializedAsync() { - _form = new AddPlayerFineRequest { PlayerId = PlayerId }; + _form = new AddFineRequest { PlayerId = PlayerId }; await GetPlayerFineDetailsAsync(); } @@ -30,7 +30,7 @@ private async Task OnValidSubmitAsync() { _processingForm = true; - var response = await HttpClient.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, _form, _cts.Token); + var response = await HttpClient.PostAsJsonAsync(AddFineRouteFactory.Uri, _form, _cts.Token); if (response.IsSuccessStatusCode) { diff --git a/src/Client/Features/Teams/AddFine.razor.cs b/src/Client/Features/Teams/AddFine.razor.cs index a8917f6..4a6ca26 100644 --- a/src/Client/Features/Teams/AddFine.razor.cs +++ b/src/Client/Features/Teams/AddFine.razor.cs @@ -9,20 +9,20 @@ public sealed partial class AddFine : IDisposable [CascadingParameter] MudDialogInstance MudDialogInstance { get; set; } = null!; [Parameter, EditorRequired] public int TeamId { get; set; } - private AddTeamFineRequest _form = null!; + private AddFineRequest _form = null!; private bool _processingForm; private readonly CancellationTokenSource _cts = new(); protected override void OnInitialized() { - _form = new AddTeamFineRequest { TeamId = TeamId }; + _form = new AddFineRequest { TeamId = TeamId }; } private async Task OnValidSubmitAsync() { _processingForm = true; - var response = await HttpClient.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, _form, _cts.Token); + var response = await HttpClient.PostAsJsonAsync(AddFineRouteFactory.Uri, _form, _cts.Token); if (response.IsSuccessStatusCode) { diff --git a/src/Server/Features/Players/AddFine.cs b/src/Server/Features/Players/AddFine.cs index 25657cf..88af321 100644 --- a/src/Server/Features/Players/AddFine.cs +++ b/src/Server/Features/Players/AddFine.cs @@ -4,39 +4,39 @@ namespace DynamoLeagueBlazor.Server.Features.Fines; [ApiController] -[Route(AddPlayerFineRouteFactory.Uri)] -public class AddPlayerFineController : ControllerBase +[Route(AddFineRouteFactory.Uri)] +public class AddFineController : ControllerBase { private readonly IMediator _mediator; private readonly IMapper _mapper; - public AddPlayerFineController(IMediator mediator, IMapper mapper) + public AddFineController(IMediator mediator, IMapper mapper) { _mediator = mediator; _mapper = mapper; } [HttpPost] - public async Task PostAsync([FromBody] AddPlayerFineRequest request, CancellationToken cancellationToken) + public async Task PostAsync([FromBody] AddFineRequest request, CancellationToken cancellationToken) { - var query = _mapper.Map(request); + var query = _mapper.Map(request); return await _mediator.Send(query, cancellationToken); } } -public record AddPlayerFineCommand(int PlayerId, string FineReason) : IRequest { } +public record AddFineCommand(int PlayerId, string FineReason) : IRequest { } -public class AddPlayerFineHandler : IRequestHandler +public class AddFineHandler : IRequestHandler { private readonly ApplicationDbContext _dbContext; - public AddPlayerFineHandler(ApplicationDbContext dbContext) + public AddFineHandler(ApplicationDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(AddPlayerFineCommand request, CancellationToken cancellationToken) + public async Task Handle(AddFineCommand request, CancellationToken cancellationToken) { var player = (await _dbContext.Players .AsTracking() @@ -52,10 +52,10 @@ public async Task Handle(AddPlayerFineCommand request, CancellationToken ca } } -public class AddPlayerFineMappingProfile : Profile +public class AddFineMappingProfile : Profile { - public AddPlayerFineMappingProfile() + public AddFineMappingProfile() { - CreateMap(); + CreateMap(); } } \ No newline at end of file diff --git a/src/Server/Features/Teams/AddFine.cs b/src/Server/Features/Teams/AddFine.cs index 87fbb1d..e4c8625 100644 --- a/src/Server/Features/Teams/AddFine.cs +++ b/src/Server/Features/Teams/AddFine.cs @@ -5,39 +5,39 @@ namespace DynamoLeagueBlazor.Server.Features.Teams; [Authorize(Policy = PolicyRequirements.Admin)] [ApiController] -[Route(AddTeamFineRouteFactory.Uri)] -public class AddTeamFineController : ControllerBase +[Route(AddFineRouteFactory.Uri)] +public class AddFineController : ControllerBase { private readonly IMediator _mediator; private readonly IMapper _mapper; - public AddTeamFineController(IMediator mediator, IMapper mapper) + public AddFineController(IMediator mediator, IMapper mapper) { _mediator = mediator; _mapper = mapper; } [HttpPost] - public async Task PostAsync([FromBody] AddTeamFineRequest request, CancellationToken cancellationToken) + public async Task PostAsync([FromBody] AddFineRequest request, CancellationToken cancellationToken) { - var query = _mapper.Map(request); + var query = _mapper.Map(request); await _mediator.Send(query, cancellationToken); } } -public record AddTeamFineCommand(int TeamId, string FineReason, decimal Amount) : IRequest { } +public record AddFineCommand(int TeamId, string FineReason, decimal Amount) : IRequest { } -public class AddTeamFineHandler : IRequestHandler +public class AddFineHandler : IRequestHandler { private readonly ApplicationDbContext _dbContext; - public AddTeamFineHandler(ApplicationDbContext dbContext) + public AddFineHandler(ApplicationDbContext dbContext) { _dbContext = dbContext; } - public async Task Handle(AddTeamFineCommand request, CancellationToken cancellationToken) + public async Task Handle(AddFineCommand request, CancellationToken cancellationToken) { var team = (await _dbContext.Teams .AsTracking() @@ -51,10 +51,10 @@ public async Task Handle(AddTeamFineCommand request, CancellationToken can } } -public class AddTeamFineMappingProfile : Profile +public class AddFineMappingProfile : Profile { - public AddTeamFineMappingProfile() + public AddFineMappingProfile() { - CreateMap(); + CreateMap(); } } diff --git a/src/Server/Program.cs b/src/Server/Program.cs index a04d141..bee77df 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -86,7 +86,7 @@ builder.Services.AddMediatR(Assembly.GetExecutingAssembly()); builder.Services.AddFluentValidation(fv => { - fv.RegisterValidatorsFromAssemblyContaining(); + fv.RegisterValidatorsFromAssemblyContaining(); }); builder.Services.AddTransient(); builder.Services.AddTransient(); diff --git a/src/Shared/Features/Players/AddFine.cs b/src/Shared/Features/Players/AddFine.cs index 7aa5b9c..a4ed63f 100644 --- a/src/Shared/Features/Players/AddFine.cs +++ b/src/Shared/Features/Players/AddFine.cs @@ -2,22 +2,22 @@ namespace DynamoLeagueBlazor.Shared.Features.Players; -public class AddPlayerFineRequest +public class AddFineRequest { public int PlayerId { get; set; } public string FineReason { get; set; } = null!; } -public class AddPlayerFineRequestValidator : AbstractValidator +public class AddFineRequestValidator : AbstractValidator { - public AddPlayerFineRequestValidator() + public AddFineRequestValidator() { RuleFor(r => r.PlayerId).GreaterThan(0); RuleFor(r => r.FineReason).NotEmpty(); } } -public class AddPlayerFineRouteFactory +public class AddFineRouteFactory { public const string Uri = "api/players/addfine"; } diff --git a/src/Shared/Features/Teams/AddFine.cs b/src/Shared/Features/Teams/AddFine.cs index b2f6cc8..398a471 100644 --- a/src/Shared/Features/Teams/AddFine.cs +++ b/src/Shared/Features/Teams/AddFine.cs @@ -1,15 +1,15 @@ namespace DynamoLeagueBlazor.Shared.Features.Teams; -public class AddTeamFineRequest +public class AddFineRequest { public int TeamId { get; set; } public string FineReason { get; set; } = null!; public int Amount { get; set; } } -public class AddTeamFineRequestValidator : AbstractValidator +public class AddFineRequestValidator : AbstractValidator { - public AddTeamFineRequestValidator() + public AddFineRequestValidator() { RuleFor(r => r.TeamId).GreaterThan(0); RuleFor(r => r.FineReason).NotEmpty(); @@ -17,7 +17,7 @@ public AddTeamFineRequestValidator() } } -public class AddTeamFineRouteFactory +public class AddFineRouteFactory { public const string Uri = "api/teams/addfine"; } \ No newline at end of file diff --git a/src/Tests/Features/Players/AddFineTests.cs b/src/Tests/Features/Players/AddFineTests.cs index e1ec481..bb8a2ff 100644 --- a/src/Tests/Features/Players/AddFineTests.cs +++ b/src/Tests/Features/Players/AddFineTests.cs @@ -6,9 +6,9 @@ namespace DynamoLeagueBlazor.Tests.Features.Fines; public class AddFineServerTests : IntegrationTestBase { - private static AddPlayerFineRequest CreateFakeValidRequest() + private static AddFineRequest CreateFakeValidRequest() { - var faker = new AutoFaker() + var faker = new AutoFaker() .RuleFor(f => f.PlayerId, 1); return faker.Generate(); @@ -22,7 +22,7 @@ public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() var client = application.CreateClient(); var stubRequest = CreateFakeValidRequest(); - var response = await client.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, stubRequest); + var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, stubRequest); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -44,7 +44,7 @@ public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() var client = application.CreateClient(); - var response = await client.PostAsJsonAsync(AddPlayerFineRouteFactory.Uri, stubRequest); + var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, stubRequest); response.StatusCode.Should().Be(HttpStatusCode.OK); @@ -67,7 +67,7 @@ public async Task GivenAnInvalidForm_ThenDoesNotSubmit() .RespondsWithJson(result) .Verifiable(); - GetHttpHandler.When(HttpMethod.Post, AddPlayerFineRouteFactory.Uri) + GetHttpHandler.When(HttpMethod.Post, AddFineRouteFactory.Uri) .Verifiable(); var cut = await RenderMudDialogAsync(); @@ -87,7 +87,7 @@ public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() .RespondsWithJson(result) .Verifiable(); - GetHttpHandler.When(HttpMethod.Post, AddPlayerFineRouteFactory.Uri) + GetHttpHandler.When(HttpMethod.Post, AddFineRouteFactory.Uri) .Respond(message => Task.FromResult(message.CreateResponse(HttpStatusCode.OK))) .Verifiable(); @@ -97,7 +97,7 @@ public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() }); // Fill the form and click submit. - var fineReason = cut.Find($"#{nameof(AddPlayerFineRequest.FineReason)}"); + var fineReason = cut.Find($"#{nameof(AddFineRequest.FineReason)}"); fineReason.Change(RandomString); var submitButton = cut.Find("button"); @@ -110,11 +110,11 @@ public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() public class AddFineRequestValidatorTests : IntegrationTestBase { - private readonly AddPlayerFineRequestValidator _validator = null!; + private readonly AddFineRequestValidator _validator = null!; public AddFineRequestValidatorTests() { - _validator = GetRequiredService(); + _validator = GetRequiredService(); } @@ -125,7 +125,7 @@ public AddFineRequestValidatorTests() [InlineData(1, "Test", true)] public void GivenDifferentRequests_ThenReturnsExpectedResult(int playerId, string reason, bool expectedResult) { - var request = new AddPlayerFineRequest { PlayerId = playerId, FineReason = reason }; + var request = new AddFineRequest { PlayerId = playerId, FineReason = reason }; var result = _validator.Validate(request); diff --git a/src/Tests/Features/Teams/AddFineTests.cs b/src/Tests/Features/Teams/AddFineTests.cs index 8a6c690..8e949f0 100644 --- a/src/Tests/Features/Teams/AddFineTests.cs +++ b/src/Tests/Features/Teams/AddFineTests.cs @@ -5,9 +5,9 @@ namespace DynamoLeagueBlazor.Tests.Features.Teams; public class AddFineServerTests : IntegrationTestBase { - private static AddTeamFineRequest CreateFakeValidRequest() + private static AddFineRequest CreateFakeValidRequest() { - var faker = new AutoFaker() + var faker = new AutoFaker() .RuleFor(f => f.TeamId, 1) .RuleFor(f => f.Amount, int.MaxValue); @@ -22,7 +22,7 @@ public async Task GivenUnauthenticatedUser_ThenDoesNotAllowAccess() var client = application.CreateClient(); var stubRequest = CreateFakeValidRequest(); - var response = await client.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, stubRequest); + var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, stubRequest); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -35,7 +35,7 @@ public async Task GivenAnAuthenticatedUser_ThenDoesNotAllowAccess() var client = application.CreateClient(); var stubRequest = CreateFakeValidRequest(); - var response = await client.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, stubRequest); + var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, stubRequest); response.StatusCode.Should().Be(HttpStatusCode.Forbidden); } @@ -53,7 +53,7 @@ public async Task GivenAnyAuthenticatedUser_WhenAValidFine_ThenSavesIt() var client = application.CreateClient(); - var response = await client.PostAsJsonAsync(AddTeamFineRouteFactory.Uri, mockRequest); + var response = await client.PostAsJsonAsync(AddFineRouteFactory.Uri, mockRequest); response.StatusCode.Should().Be(HttpStatusCode.OK); @@ -71,7 +71,7 @@ public class AddFineUITests : UITestBase [Fact] public async Task GivenAnInvalidForm_ThenDoesNotSubmit() { - GetHttpHandler.When(HttpMethod.Post, AddTeamFineRouteFactory.Uri) + GetHttpHandler.When(HttpMethod.Post, AddFineRouteFactory.Uri) .Verifiable(); var cut = await RenderMudDialogAsync(); @@ -85,7 +85,7 @@ public async Task GivenAnInvalidForm_ThenDoesNotSubmit() [Fact] public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() { - GetHttpHandler.When(HttpMethod.Post, AddTeamFineRouteFactory.Uri) + GetHttpHandler.When(HttpMethod.Post, AddFineRouteFactory.Uri) .Respond(message => Task.FromResult(message.CreateResponse(HttpStatusCode.OK))) .Verifiable(); @@ -95,10 +95,10 @@ public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() }); // Fill the form and click submit. - var amount = cut.Find($"#{nameof(AddTeamFineRequest.Amount)}"); + var amount = cut.Find($"#{nameof(AddFineRequest.Amount)}"); amount.Change(RandomPositiveNumber); - var fineReason = cut.Find($"#{nameof(AddTeamFineRequest.FineReason)}"); + var fineReason = cut.Find($"#{nameof(AddFineRequest.FineReason)}"); fineReason.Change(RandomString); var submitButton = cut.Find("button"); @@ -112,40 +112,40 @@ public async Task GivenAValidForm_WhenSubmitIsClicked_ThenSavesTheForm() #pragma warning disable xUnit1012 // Null should not be used for value type parameters public class AddFineRequestValidatorTests : IntegrationTestBase { - private readonly AddTeamFineRequestValidator _validator = null!; + private readonly AddFineRequestValidator _validator = null!; public AddFineRequestValidatorTests() { - _validator = GetRequiredService(); + _validator = GetRequiredService(); } [Theory] [InlineData(null), InlineData(-1), InlineData(0)] public void GivenInvalidTeamIds_ThenAreNotValid(int teamId) - => _validator.TestValidate(new AddTeamFineRequest { TeamId = teamId }).ShouldHaveValidationErrorFor(v => v.TeamId); + => _validator.TestValidate(new AddFineRequest { TeamId = teamId }).ShouldHaveValidationErrorFor(v => v.TeamId); [Fact] public void GivenAValidTeamId_ThenIsValid() - => _validator.TestValidate(new AddTeamFineRequest { TeamId = 1 }).ShouldNotHaveValidationErrorFor(v => v.TeamId); + => _validator.TestValidate(new AddFineRequest { TeamId = 1 }).ShouldNotHaveValidationErrorFor(v => v.TeamId); [Theory] [InlineData(null), InlineData("")] public void GivenInvalidFineReasons_ThenIsNotValid(string fineReason) - => _validator.TestValidate(new AddTeamFineRequest { FineReason = fineReason }).ShouldHaveValidationErrorFor(v => v.FineReason); + => _validator.TestValidate(new AddFineRequest { FineReason = fineReason }).ShouldHaveValidationErrorFor(v => v.FineReason); [Fact] public void GivenAValidFineReason_ThenIsValid() - => _validator.TestValidate(new AddTeamFineRequest { FineReason = RandomString }).ShouldNotHaveValidationErrorFor(v => v.FineReason); + => _validator.TestValidate(new AddFineRequest { FineReason = RandomString }).ShouldNotHaveValidationErrorFor(v => v.FineReason); [Theory] [InlineData(null), InlineData(-1), InlineData(0)] public void GivenInvalidAmounts_ThenAreNotValid(int amount) - => _validator.TestValidate(new AddTeamFineRequest { Amount = amount }).ShouldHaveValidationErrorFor(v => v.Amount); + => _validator.TestValidate(new AddFineRequest { Amount = amount }).ShouldHaveValidationErrorFor(v => v.Amount); [Fact] public void GivenAValidAmount_ThenIsValid() - => _validator.TestValidate(new AddTeamFineRequest { Amount = 1 }).ShouldNotHaveValidationErrorFor(v => v.Amount); + => _validator.TestValidate(new AddFineRequest { Amount = 1 }).ShouldNotHaveValidationErrorFor(v => v.Amount); } #pragma warning restore xUnit1012 // Null should not be used for value type parameters \ No newline at end of file