Events-MVC (example with htmx)

This commit is contained in:
Boris Milašinović
2026-04-25 22:21:35 +02:00
parent eb04483417
commit 0ee1b22f61
114 changed files with 7966 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
#if POSTGRES
using Events.EF.Data.Postgres;
#else
using Events.EF.Data.MSSQL;
#endif
using Events.EF.Models;
using Events.MVC.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Sieve.Models;
using Sieve.Services;
namespace Events.Tests.UnitTests.Infrastructure;
internal static class ControllerTestContext
{
public static EventsContext CreateContext()
{
var options = new DbContextOptionsBuilder<EventsContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
return new EventsContext(options);
}
public static IOptions<PagingSettings> CreatePagingOptions(int pageSize = 10)
{
return Options.Create(new PagingSettings { PageSize = pageSize });
}
public static SieveModel EmptySieveModel()
{
return new SieveModel();
}
public static ISieveProcessor CreateSieveProcessor()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddOptions();
services.AddScoped<ISieveProcessor, SieveProcessor>();
using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();
return scope.ServiceProvider.GetRequiredService<ISieveProcessor>();
}
public static Country CreateCountry(string code = "HR", string alpha3 = "HRV", string name = "Croatia")
{
return new Country
{
Code = code,
Alpha3 = alpha3,
Name = name
};
}
public static Person CreatePerson(int id = 1, string countryCode = "HR", string firstName = "Ivan", string lastName = "Horvat")
{
return new Person
{
Id = id,
FirstName = firstName,
LastName = lastName,
FirstNameTranscription = firstName,
LastNameTranscription = lastName,
AddressLine = "Ilica 1",
PostalCode = "10000",
City = "Zagreb",
AddressCountry = "Croatia",
Email = $"{firstName.ToLowerInvariant()}.{lastName.ToLowerInvariant()}@example.com",
ContactPhone = "+38591111222",
BirthDate = new DateOnly(1990, 5, 1),
DocumentNumber = $"DOC-{id}",
CountryCode = countryCode
};
}
public static Event CreateEvent(int id = 100, string name = "Spring Games")
{
return new Event
{
Id = id,
Name = name,
EventDate = new DateOnly(2026, 4, 15)
};
}
public static Sport CreateSport(int id = 10, string name = "Football")
{
return new Sport
{
Id = id,
Name = name
};
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Events.Tests.UnitTests.Infrastructure;
internal static class ControllerTestExtensions
{
public static T WithTempData<T>(this T controller) where T : Controller
{
var httpContext = new DefaultHttpContext();
controller.ControllerContext = new ControllerContext
{
HttpContext = httpContext
};
controller.TempData = new TempDataDictionary(httpContext, new TestTempDataProvider());
return controller;
}
}

View File

@@ -0,0 +1,79 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Npgsql;
namespace Events.Tests.UnitTests.Infrastructure;
public class ProviderSpecificQueryShould
{
[Fact]
public async Task ReturnMatchingRowsWhenUsingILikeWithInMemoryProvider()
{
await using var ctx = ControllerTestContext.CreateContext();
ctx.Countries.Add(ControllerTestContext.CreateCountry());
ctx.People.Add(ControllerTestContext.CreatePerson());
await ctx.SaveChangesAsync();
var people = await ctx.People
.Where(person => person.FirstName != null && Microsoft.EntityFrameworkCore.EF.Functions.ILike(person.FirstName, "%iv%"))
.ToListAsync();
Assert.Single(people);
Assert.Equal("Ivan", people[0].FirstName);
}
[Fact]
public async Task ThrowInvalidOperationExceptionWhenUsingILikeWithInMemoryProvider()
{
await using var ctx = ControllerTestContext.CreateContext();
ctx.Countries.Add(ControllerTestContext.CreateCountry());
ctx.People.Add(ControllerTestContext.CreatePerson());
await ctx.SaveChangesAsync();
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await ctx.People
.Where(person => person.FirstName != null && Microsoft.EntityFrameworkCore.EF.Functions.ILike(person.FirstName, "%iv%"))
.ToListAsync());
Assert.Contains("ILike", exception.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task ExecuteILikeWhenUsingPostgreSqlProvider()
{
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false)
.AddUserSecrets<ProviderSpecificQueryShould>(optional: true)
.Build();
var productionConnectionString = configuration.GetConnectionString("EventDB-Test");
Assert.False(
string.IsNullOrWhiteSpace(productionConnectionString),
"The EventDB-Test connection string must be available so the PostgreSQL-backed provider test can connect to the PostgreSQL copy.");
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(productionConnectionString)
{
SslMode = SslMode.Disable
};
var options = new DbContextOptionsBuilder<Events.EF.Data.Postgres.EventsContext>()
.UseNpgsql(connectionStringBuilder.ConnectionString)
.Options;
await using var ctx = new Events.EF.Data.Postgres.EventsContext(options);
var people = await ctx.People
.Where(person => person.FirstName != null && Microsoft.EntityFrameworkCore.EF.Functions.ILike(person.FirstName, "%iv%"))
.Take(20)
.ToListAsync();
Assert.NotEmpty(people);
Assert.All(
people,
person => Assert.Contains(
"iv",
person.FirstName,
StringComparison.OrdinalIgnoreCase));
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Events.Tests.UnitTests.Infrastructure;
internal sealed class TestTempDataProvider : ITempDataProvider
{
private Dictionary<string, object> values = [];
public IDictionary<string, object> LoadTempData(HttpContext context)
{
return new Dictionary<string, object>(values);
}
public void SaveTempData(HttpContext context, IDictionary<string, object> values)
{
this.values = new Dictionary<string, object>(values);
}
}