Events-MVC (example with htmx)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
#if POSTGRES
|
||||
using Events.EF.Data.Postgres;
|
||||
#else
|
||||
using Events.EF.Data.MSSQL;
|
||||
#endif
|
||||
using Events.EF.Models;
|
||||
using Events.MVC.Controllers;
|
||||
using Events.MVC.Models.Countries;
|
||||
using Events.Tests.UnitTests.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Events.Tests.UnitTests.Controllers;
|
||||
|
||||
public class CountriesControllerShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task ReturnPartialViewWithExpectedViewModelForExistingRow()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Row("HR");
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_CountryRow", partial.ViewName);
|
||||
Assert.Equal("Croatia", Assert.IsType<CountryViewModel>(partial.Model).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateCountry()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Create(
|
||||
new CountryViewModel
|
||||
{
|
||||
Code = "de",
|
||||
Alpha3 = "deu",
|
||||
Name = "Germany",
|
||||
Translations =
|
||||
[
|
||||
new CountryTranslationViewModel { LanguageCode = "hr", Name = "Germany" }
|
||||
]
|
||||
},
|
||||
ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_CountriesList", partial.ViewName);
|
||||
var country = await ctx.Countries.SingleAsync();
|
||||
Assert.Equal("DE", country.Code);
|
||||
Assert.Equal("Germany", country.Name);
|
||||
Assert.Contains("was added successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditCountry()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Edit("HR", new CountryViewModel
|
||||
{
|
||||
Code = "HR",
|
||||
Alpha3 = "HRV",
|
||||
Name = "Republic of Croatia",
|
||||
Translations = []
|
||||
});
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_CountryRow", partial.ViewName);
|
||||
Assert.Equal("Republic of Croatia", (await ctx.Countries.SingleAsync()).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteCountry()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete("HR", ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_CountriesList", partial.ViewName);
|
||||
Assert.Empty(ctx.Countries);
|
||||
Assert.Contains("was deleted successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnConflictWhenDeletingCountryWithRelatedPeople()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete("HR", ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var content = Assert.IsType<ContentResult>(result);
|
||||
Assert.Equal(409, controller.Response.StatusCode);
|
||||
Assert.Equal("The country cannot be deleted because related people exist.", content.Content);
|
||||
}
|
||||
|
||||
private static CountriesController CreateController(EventsContext ctx, bool useSieve = true)
|
||||
{
|
||||
return new CountriesController(
|
||||
ctx,
|
||||
useSieve ? ControllerTestContext.CreateSieveProcessor() : null!,
|
||||
ControllerTestContext.CreatePagingOptions())
|
||||
.WithTempData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#if POSTGRES
|
||||
using Events.EF.Data.Postgres;
|
||||
#else
|
||||
using Events.EF.Data.MSSQL;
|
||||
#endif
|
||||
using Events.EF.Models;
|
||||
using Events.MVC.Controllers;
|
||||
using Events.MVC.Models.Events;
|
||||
using Events.Tests.UnitTests.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Events.Tests.UnitTests.Controllers;
|
||||
|
||||
public class EventsControllerShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task ReturnPartialViewWithExpectedViewModelForExistingRow()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Row(100);
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_EventRow", partial.ViewName);
|
||||
Assert.Equal("Spring Games", Assert.IsType<EventViewModel>(partial.Model).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateEvent()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Create(
|
||||
new EventViewModel { Name = "Autumn Cup", EventDate = new DateOnly(2026, 9, 10) },
|
||||
ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_EventsList", partial.ViewName);
|
||||
Assert.Contains(ctx.Events, e => e.Name == "Autumn Cup");
|
||||
Assert.Contains("was added successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditEvent()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Edit(100, new EventViewModel { Id = 100, Name = "Updated Games", EventDate = new DateOnly(2026, 5, 20) });
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_EventRow", partial.ViewName);
|
||||
Assert.Equal("Updated Games", (await ctx.Events.SingleAsync()).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteEvent()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(100, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_EventsList", partial.ViewName);
|
||||
Assert.Empty(ctx.Events);
|
||||
Assert.Contains("was deleted successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnConflictWhenDeletingEventWithRegistrations()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport());
|
||||
ctx.Registrations.Add(new Registration { Id = 1000, EventId = 100, PersonId = 1, SportId = 10 });
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(100, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var content = Assert.IsType<ContentResult>(result);
|
||||
Assert.Equal(409, controller.Response.StatusCode);
|
||||
Assert.Equal("The event cannot be deleted because registrations exist.", content.Content);
|
||||
}
|
||||
|
||||
private static EventsController CreateController(EventsContext ctx, bool useSieve = true)
|
||||
{
|
||||
return new EventsController(
|
||||
ctx,
|
||||
useSieve ? ControllerTestContext.CreateSieveProcessor() : null!,
|
||||
ControllerTestContext.CreatePagingOptions())
|
||||
.WithTempData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
#if POSTGRES
|
||||
using Events.EF.Data.Postgres;
|
||||
#else
|
||||
using Events.EF.Data.MSSQL;
|
||||
#endif
|
||||
using Events.EF.Models;
|
||||
using Events.MVC.Controllers;
|
||||
using Events.MVC.Models;
|
||||
using Events.MVC.Models.People;
|
||||
using Events.Tests.UnitTests.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Sieve.Models;
|
||||
|
||||
namespace Events.Tests.UnitTests.Controllers;
|
||||
|
||||
public class PeopleControllerShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task RedirectToCountriesAndSetToastWhenIndexIsRequestedWithoutCountries()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Index(new SieveModel());
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(result);
|
||||
Assert.Equal("Index", redirect.ActionName);
|
||||
Assert.Equal("Countries", redirect.ControllerName);
|
||||
Assert.Equal(Events.MVC.Constants.Messages.CountriesRequiredForPeople, controller.TempData[Events.MVC.Constants.TempDataKeys.ToastMessage]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreatePerson()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Create(
|
||||
new PersonViewModel
|
||||
{
|
||||
FirstName = "Ana",
|
||||
LastName = "Kovac",
|
||||
FirstNameTranscription = "Ana",
|
||||
LastNameTranscription = "Kovac",
|
||||
AddressLine = "Main Street 1",
|
||||
PostalCode = "10000",
|
||||
City = "Zagreb",
|
||||
AddressCountry = "Croatia",
|
||||
Email = "ana.kovac@example.com",
|
||||
ContactPhone = "+38591123456",
|
||||
BirthDate = new DateOnly(1995, 1, 1),
|
||||
DocumentNumber = "DOC-2",
|
||||
CountryCode = "hr"
|
||||
},
|
||||
ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_PeopleList", partial.ViewName);
|
||||
var model = Assert.IsType<PeoplePageViewModel>(partial.Model);
|
||||
Assert.Contains(model.People.Data, p => p.FullName == "Ana Kovac" && p.CountryName == "Croatia");
|
||||
Assert.Contains("was added successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnPartialViewWithExpectedPeopleListForPersonAddedBeforeIndexRead()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
await controller.Create(
|
||||
new PersonViewModel
|
||||
{
|
||||
FirstName = "Ana",
|
||||
LastName = "Kovac",
|
||||
FirstNameTranscription = "Ana",
|
||||
LastNameTranscription = "Kovac",
|
||||
AddressLine = "Main Street 1",
|
||||
PostalCode = "10000",
|
||||
City = "Zagreb",
|
||||
AddressCountry = "Croatia",
|
||||
Email = "ana.kovac@example.com",
|
||||
ContactPhone = "+38591123456",
|
||||
BirthDate = new DateOnly(1995, 1, 1),
|
||||
DocumentNumber = "DOC-2",
|
||||
CountryCode = "hr"
|
||||
},
|
||||
ControllerTestContext.EmptySieveModel());
|
||||
|
||||
controller.Request.Headers[Events.MVC.Constants.HtmxHeaders.Request] = "true";
|
||||
|
||||
var result = await controller.Index(new SieveModel
|
||||
{
|
||||
Filters = "FirstName==Ana"
|
||||
});
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_PeopleList", partial.ViewName);
|
||||
var model = Assert.IsType<PeoplePageViewModel>(partial.Model);
|
||||
var person = Assert.Single(model.People.Data);
|
||||
Assert.Equal("Ana Kovac", person.FullName);
|
||||
Assert.Equal("Croatia", person.CountryName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditPerson()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Edit(1, new PersonViewModel
|
||||
{
|
||||
Id = 1,
|
||||
FirstName = "Ivan",
|
||||
LastName = "Kovac",
|
||||
FirstNameTranscription = "Ivan",
|
||||
LastNameTranscription = "Kovac",
|
||||
AddressLine = "Updated Street 2",
|
||||
PostalCode = "10000",
|
||||
City = "Zagreb",
|
||||
AddressCountry = "Croatia",
|
||||
Email = "ivan.kovac@example.com",
|
||||
ContactPhone = "+38591111222",
|
||||
BirthDate = new DateOnly(1990, 5, 1),
|
||||
DocumentNumber = "DOC-1",
|
||||
CountryCode = "HR"
|
||||
});
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_PersonRow", partial.ViewName);
|
||||
Assert.Equal("Kovac", (await ctx.People.SingleAsync()).LastName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeletePerson()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(1, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_PeopleList", partial.ViewName);
|
||||
Assert.Empty(ctx.People);
|
||||
Assert.Contains("was deleted successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FilterPeopleByTranscribedFullName()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.AddRange(
|
||||
ControllerTestContext.CreatePerson(id: 1, firstName: "Ђорђе", lastName: "Петровић"),
|
||||
ControllerTestContext.CreatePerson(id: 2, firstName: "Ana", lastName: "Kovac"));
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var person = await ctx.People.SingleAsync(p => p.Id == 1);
|
||||
person.FirstNameTranscription = "Djordje";
|
||||
person.LastNameTranscription = "Petrovic";
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Index(new SieveModel
|
||||
{
|
||||
Filters = "FullNameTranscription@=*Petrovic"
|
||||
});
|
||||
|
||||
var view = Assert.IsType<ViewResult>(result);
|
||||
var model = Assert.IsType<PeoplePageViewModel>(view.Model);
|
||||
var people = Assert.Single(model.People.Data);
|
||||
Assert.Equal(1, people.Id);
|
||||
Assert.Equal("Ђорђе Петровић", people.FullName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FilterPeopleByCountry()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.AddRange(
|
||||
ControllerTestContext.CreateCountry(),
|
||||
ControllerTestContext.CreateCountry(code: "SI", alpha3: "SVN", name: "Slovenia"));
|
||||
ctx.People.AddRange(
|
||||
ControllerTestContext.CreatePerson(id: 1, countryCode: "HR", firstName: "Ivan", lastName: "Horvat"),
|
||||
ControllerTestContext.CreatePerson(id: 2, countryCode: "SI", firstName: "Ana", lastName: "Novak"));
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Index(new SieveModel
|
||||
{
|
||||
Filters = "CountryCode==SI"
|
||||
});
|
||||
|
||||
var view = Assert.IsType<ViewResult>(result);
|
||||
var model = Assert.IsType<PeoplePageViewModel>(view.Model);
|
||||
var person = Assert.Single(model.People.Data);
|
||||
Assert.Equal("Ana Novak", person.FullName);
|
||||
Assert.Equal("SI", model.CountryFilter);
|
||||
Assert.Contains(model.CountryOptions, option => option.Value == "SI" && option.Selected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnConflictWhenDeletingPersonWithRegistrations()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport());
|
||||
ctx.Registrations.Add(new Registration
|
||||
{
|
||||
Id = 1000,
|
||||
EventId = 100,
|
||||
PersonId = 1,
|
||||
SportId = 10
|
||||
});
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(1, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var content = Assert.IsType<ContentResult>(result);
|
||||
Assert.Equal(409, controller.Response.StatusCode);
|
||||
Assert.Equal("The person cannot be deleted because registrations exist.", content.Content);
|
||||
}
|
||||
|
||||
private static PeopleController CreateController(EventsContext ctx, bool useSieve = true)
|
||||
{
|
||||
return new PeopleController(
|
||||
ctx,
|
||||
useSieve ? ControllerTestContext.CreateSieveProcessor() : null!,
|
||||
ControllerTestContext.CreatePagingOptions())
|
||||
.WithTempData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#if POSTGRES
|
||||
using Events.EF.Data.Postgres;
|
||||
#else
|
||||
using Events.EF.Data.MSSQL;
|
||||
#endif
|
||||
using Events.EF.Models;
|
||||
using Events.MVC.Controllers;
|
||||
using Events.MVC.Models.Registrations;
|
||||
using Events.Tests.UnitTests.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Sieve.Models;
|
||||
|
||||
namespace Events.Tests.UnitTests.Controllers;
|
||||
|
||||
public class RegistrationsControllerShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task RedirectToEventsWhenIndexIsRequestedWithoutEvents()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Index(null, new SieveModel());
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(result);
|
||||
Assert.Equal("Events", redirect.ControllerName);
|
||||
Assert.Equal(Events.MVC.Constants.Messages.EventsRequiredForRegistrations, controller.TempData[Events.MVC.Constants.TempDataKeys.ToastMessage]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToSportsWhenIndexIsRequestedWithoutSports()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Events.Add(new Event { Id = 1, Name = "Event 1", EventDate = new DateOnly(2026, 3, 23) });
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Index(1, new SieveModel());
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(result);
|
||||
Assert.Equal("Sports", redirect.ControllerName);
|
||||
Assert.Equal(Events.MVC.Constants.Messages.SportsRequiredForRegistrations, controller.TempData[Events.MVC.Constants.TempDataKeys.ToastMessage]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToPeopleWhenIndexIsRequestedWithoutPeople()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Events.Add(new Event { Id = 1, Name = "Event 1", EventDate = new DateOnly(2026, 3, 23) });
|
||||
ctx.Sports.Add(new Sport { Id = 1, Name = "Football" });
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Index(1, new SieveModel());
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(result);
|
||||
Assert.Equal("People", redirect.ControllerName);
|
||||
Assert.Equal(Events.MVC.Constants.Messages.PeopleRequiredForRegistrations, controller.TempData[Events.MVC.Constants.TempDataKeys.ToastMessage]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateRegistration()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
SeedRegistrationDependencies(ctx);
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Create(
|
||||
new RegistrationViewModel
|
||||
{
|
||||
EventId = 100,
|
||||
PersonId = 1,
|
||||
SportId = 10
|
||||
},
|
||||
ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_RegistrationsPanel", partial.ViewName);
|
||||
Assert.Single(ctx.Registrations);
|
||||
Assert.Contains("Registration was added successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditRegistration()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
SeedRegistrationDependencies(ctx);
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson(id: 2, firstName: "Ana", lastName: "Kovac"));
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport(id: 20, name: "Volleyball"));
|
||||
ctx.Registrations.Add(new Registration { Id = 1000, EventId = 100, PersonId = 1, SportId = 10 });
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Edit(1000, new RegistrationViewModel
|
||||
{
|
||||
Id = 1000,
|
||||
EventId = 100,
|
||||
PersonId = 2,
|
||||
SportId = 20
|
||||
});
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_RegistrationRow", partial.ViewName);
|
||||
var registration = await ctx.Registrations.SingleAsync();
|
||||
Assert.Equal(2, registration.PersonId);
|
||||
Assert.Equal(20, registration.SportId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteRegistration()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
SeedRegistrationDependencies(ctx);
|
||||
ctx.Registrations.Add(new Registration { Id = 1000, EventId = 100, PersonId = 1, SportId = 10 });
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(1000, 100, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_RegistrationsPanel", partial.ViewName);
|
||||
Assert.Empty(ctx.Registrations);
|
||||
Assert.Contains("Registration was deleted successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnConflictWhenCreatingRegistrationWithoutDependencies()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Create(new RegistrationViewModel { EventId = 100, PersonId = 1, SportId = 10 }, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var content = Assert.IsType<ContentResult>(result);
|
||||
Assert.Equal(409, controller.Response.StatusCode);
|
||||
Assert.Equal("At least one event, one person, and one sport are required before adding registrations.", content.Content);
|
||||
}
|
||||
|
||||
private static RegistrationsController CreateController(EventsContext ctx, bool useSieve = true)
|
||||
{
|
||||
return new RegistrationsController(
|
||||
ctx,
|
||||
useSieve ? ControllerTestContext.CreateSieveProcessor() : null!,
|
||||
ControllerTestContext.CreatePagingOptions())
|
||||
.WithTempData();
|
||||
}
|
||||
|
||||
private static void SeedRegistrationDependencies(EventsContext ctx)
|
||||
{
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
#if POSTGRES
|
||||
using Events.EF.Data.Postgres;
|
||||
#else
|
||||
using Events.EF.Data.MSSQL;
|
||||
#endif
|
||||
using Events.EF.Models;
|
||||
using Events.MVC.Controllers;
|
||||
using Events.MVC.Models;
|
||||
using Events.MVC.Models.Sports;
|
||||
using Events.Tests.UnitTests.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Sieve.Models;
|
||||
|
||||
namespace Events.Tests.UnitTests.Controllers;
|
||||
|
||||
public class SportsControllerShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task ReturnPartialViewWithExpectedViewModelForExistingRow()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Sports.Add(new Sport { Id = 5, Name = "Basketball" });
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Row(5);
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_SportRow", partial.ViewName);
|
||||
var model = Assert.IsType<SportViewModel>(partial.Model);
|
||||
Assert.Equal(5, model.Id);
|
||||
Assert.Equal("Basketball", model.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnNotFoundForMissingRow()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Row(404);
|
||||
|
||||
Assert.IsType<NotFoundResult>(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateSport()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Create(new SportViewModel { Name = "Volleyball" }, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_SportsList", partial.ViewName);
|
||||
Assert.Contains(ctx.Sports, s => s.Name == "Volleyball");
|
||||
Assert.Contains("was added successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnPagedSportsWhenIndexIsRequestedUsingMockedPagingOptions()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Sports.AddRange(
|
||||
new Sport { Id = 1, Name = "Athletics" },
|
||||
new Sport { Id = 2, Name = "Basketball" },
|
||||
new Sport { Id = 3, Name = "Cycling" });
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var optionsMock = new Mock<IOptions<PagingSettings>>();
|
||||
optionsMock
|
||||
.SetupGet(options => options.Value)
|
||||
.Returns(new PagingSettings { PageSize = 2 });
|
||||
|
||||
var controller = new SportsController(
|
||||
ctx,
|
||||
ControllerTestContext.CreateSieveProcessor(),
|
||||
optionsMock.Object)
|
||||
.WithTempData();
|
||||
|
||||
var result = await controller.Index(new SieveModel());
|
||||
|
||||
var view = Assert.IsType<ViewResult>(result);
|
||||
var model = Assert.IsType<PagedList<SportViewModel>>(view.Model);
|
||||
Assert.Equal(2, model.Data.Count);
|
||||
Assert.Equal(2, model.PagingInfo.ItemsPerPage);
|
||||
Assert.Equal(3, model.PagingInfo.TotalItemsCount);
|
||||
Assert.Equal(3, model.PagingInfo.FilteredItemsCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PopulateModelStateValidationErrorsForMissingName()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
var controller = CreateController(ctx);
|
||||
var invalidModel = new SportViewModel { Name = string.Empty };
|
||||
|
||||
var result = await controller.Create(invalidModel, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
Assert.False(
|
||||
controller.ModelState.IsValid,
|
||||
"This assertion intentionally demonstrates an incorrect expectation: unit tests do not run the MVC validation pipeline automatically.");
|
||||
Assert.True(
|
||||
controller.ModelState.ContainsKey(nameof(SportViewModel.Name)),
|
||||
"This assertion intentionally demonstrates an incorrect expectation: without MVC model validation, ModelState should not contain a validation entry for Name.");
|
||||
Assert.Contains(
|
||||
controller.ModelState[nameof(SportViewModel.Name)]!.Errors,
|
||||
error => error.ErrorMessage == "The Name field is required.");
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_CreateSportForm", partial.ViewName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditSport()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx, useSieve: false);
|
||||
|
||||
var result = await controller.Edit(10, new SportViewModel { Id = 10, Name = "Volleyball" });
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_SportRow", partial.ViewName);
|
||||
Assert.Equal("Volleyball", (await ctx.Sports.SingleAsync()).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteSport()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport());
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(10, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var partial = Assert.IsType<PartialViewResult>(result);
|
||||
Assert.Equal("_SportsList", partial.ViewName);
|
||||
Assert.Empty(ctx.Sports);
|
||||
Assert.Contains("was deleted successfully", controller.Response.Headers[Events.MVC.Constants.HtmxHeaders.Trigger].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnConflictWhenDeletingSportWithRegistrations()
|
||||
{
|
||||
await using var ctx = ControllerTestContext.CreateContext();
|
||||
ctx.Countries.Add(ControllerTestContext.CreateCountry());
|
||||
ctx.People.Add(ControllerTestContext.CreatePerson());
|
||||
ctx.Events.Add(ControllerTestContext.CreateEvent());
|
||||
ctx.Sports.Add(ControllerTestContext.CreateSport());
|
||||
ctx.Registrations.Add(new Registration
|
||||
{
|
||||
Id = 1000,
|
||||
EventId = 100,
|
||||
PersonId = 1,
|
||||
SportId = 10
|
||||
});
|
||||
await ctx.SaveChangesAsync();
|
||||
var controller = CreateController(ctx);
|
||||
|
||||
var result = await controller.Delete(10, ControllerTestContext.EmptySieveModel());
|
||||
|
||||
var content = Assert.IsType<ContentResult>(result);
|
||||
Assert.Equal(409, controller.Response.StatusCode);
|
||||
Assert.Equal("The sport cannot be deleted because registrations exist.", content.Content);
|
||||
}
|
||||
|
||||
private static SportsController CreateController(EventsContext ctx, bool useSieve = true)
|
||||
{
|
||||
var controller = new SportsController(
|
||||
ctx,
|
||||
useSieve ? ControllerTestContext.CreateSieveProcessor() : null!,
|
||||
ControllerTestContext.CreatePagingOptions())
|
||||
.WithTempData();
|
||||
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user