Init
This commit is contained in:
146
Prefab.Tests/Unit/Catalog/CatalogSeederShould.cs
Normal file
146
Prefab.Tests/Unit/Catalog/CatalogSeederShould.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Prefab.Catalog.Api.Data;
|
||||
using Prefab.Catalog.Data;
|
||||
using Prefab.Catalog.Data.Services;
|
||||
using Prefab.Catalog.Domain.Entities;
|
||||
using Prefab.Catalog.Domain.Services;
|
||||
using Prefab.Data;
|
||||
using Prefab.Handler;
|
||||
using Shouldly;
|
||||
|
||||
namespace Prefab.Tests.Unit.Catalog;
|
||||
|
||||
public sealed class CatalogSeederShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task PopulateCatalogDataInSqliteDatabase()
|
||||
{
|
||||
await using var connection = new SqliteConnection("DataSource=:memory:");
|
||||
await connection.OpenAsync(TestContext.Current.CancellationToken);
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IHandlerContextAccessor, HandlerContextAccessor>();
|
||||
|
||||
services.AddDbContext<TestCatalogDb>(options =>
|
||||
{
|
||||
options.UseSqlite(connection);
|
||||
});
|
||||
|
||||
services.AddDbContextFactory<TestCatalogDb>(options =>
|
||||
{
|
||||
options.UseSqlite(connection);
|
||||
}, ServiceLifetime.Scoped);
|
||||
|
||||
services.AddScoped<AppDb>(sp => sp.GetRequiredService<TestCatalogDb>());
|
||||
services.AddScoped<IPrefabDb>(sp => sp.GetRequiredService<TestCatalogDb>());
|
||||
services.AddScoped<IModuleDb>(sp => sp.GetRequiredService<TestCatalogDb>());
|
||||
services.AddScoped<IModuleDbReadOnly>(sp => sp.GetRequiredService<TestCatalogDb>());
|
||||
services.AddScoped<ICatalogDbContextFactory, TestCatalogDbFactory>();
|
||||
services.AddScoped<IUniqueChecker, UniqueChecker>();
|
||||
services.AddSingleton<ILogger<Seeder>>(_ => NullLogger<Seeder>.Instance);
|
||||
services.AddScoped<Seeder>();
|
||||
|
||||
await using var provider = services.BuildServiceProvider();
|
||||
|
||||
await using (var scope = provider.CreateAsyncScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<TestCatalogDb>();
|
||||
await db.Database.EnsureCreatedAsync(TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
await using (var scope = provider.CreateAsyncScope())
|
||||
{
|
||||
var seeder = scope.ServiceProvider.GetRequiredService<Seeder>();
|
||||
await seeder.Execute(scope.ServiceProvider, TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
await using (var scope = provider.CreateAsyncScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<TestCatalogDb>();
|
||||
|
||||
var allCategories = await db.Categories
|
||||
.Where(c => c.DeletedOn == null)
|
||||
.ToListAsync(TestContext.Current.CancellationToken);
|
||||
|
||||
allCategories.ShouldNotBeEmpty("Catalog seeder should create categories.");
|
||||
|
||||
var categories = allCategories
|
||||
.Select(c => c.Slug)
|
||||
.Where(slug => !string.IsNullOrWhiteSpace(slug))
|
||||
.Select(slug => slug!.Trim())
|
||||
.ToList();
|
||||
|
||||
var expectedSlugs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"ceiling-supports",
|
||||
"boxes-and-covers",
|
||||
"prefab-assemblies",
|
||||
"rod-strut-hardware"
|
||||
};
|
||||
|
||||
categories.ShouldBeSubsetOf(expectedSlugs, "Seeded catalog should only include the expected root categories.");
|
||||
categories.Count.ShouldBe(expectedSlugs.Count, "Seeded catalog should include all expected root categories.");
|
||||
|
||||
var products = await db.Products
|
||||
.Where(p => p.DeletedOn == null && p.Kind == ProductKind.Model)
|
||||
.Include(p => p.Variants)
|
||||
.Select(p => new { p.Slug, p.Name, VariantCount = p.Variants.Count })
|
||||
.ToListAsync(TestContext.Current.CancellationToken);
|
||||
|
||||
products.ShouldNotBeEmpty("Catalog seeder should introduce product models for listing.");
|
||||
products.Any(p => string.Equals(p.Slug, "ceiling-tbar-box-assembly", StringComparison.OrdinalIgnoreCase)).ShouldBeTrue();
|
||||
products.Any(p => string.Equals(p.Slug, "fan-hanger-assembly", StringComparison.OrdinalIgnoreCase)).ShouldBeTrue();
|
||||
products.Any(p => p.VariantCount > 0).ShouldBeTrue("Seed should include at least one model with variants.");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TestCatalogDb : AppDb
|
||||
{
|
||||
public TestCatalogDb(DbContextOptions<TestCatalogDb> options, IHandlerContextAccessor accessor)
|
||||
: base(options, accessor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void PrefabOnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.PrefabOnModelCreating(builder);
|
||||
|
||||
foreach (var entity in builder.Model.GetEntityTypes())
|
||||
{
|
||||
var rowVersionProperty = entity.FindProperty("RowVersion");
|
||||
if (rowVersionProperty is not null)
|
||||
{
|
||||
rowVersionProperty.IsNullable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added))
|
||||
{
|
||||
var rowVersion = entry.Properties.FirstOrDefault(p => string.Equals(p.Metadata.Name, "RowVersion", StringComparison.Ordinal));
|
||||
if (rowVersion is not null && rowVersion.CurrentValue is null)
|
||||
{
|
||||
rowVersion.CurrentValue = Guid.NewGuid().ToByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
return base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TestCatalogDbFactory(IDbContextFactory<TestCatalogDb> dbFactory) : ICatalogDbContextFactory
|
||||
{
|
||||
public async ValueTask<IModuleDb> CreateWritableAsync(CancellationToken cancellationToken = default) =>
|
||||
await dbFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
public async ValueTask<IModuleDbReadOnly> CreateReadOnlyAsync(CancellationToken cancellationToken = default) =>
|
||||
await dbFactory.CreateDbContextAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user