This commit is contained in:
2025-10-27 17:39:18 -04:00
commit 31f723bea4
1579 changed files with 642409 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
using Microsoft.EntityFrameworkCore;
using Prefab.Catalog.Api.Data;
using Prefab.Catalog.Domain.Entities;
using Prefab.Catalog.Domain.Services;
using Prefab.Handler;
using Shouldly;
namespace Prefab.Tests.Unit.Modules.Catalog.Data;
public sealed class ProductConcurrencyShould : IAsyncLifetime, IDisposable
{
private readonly HandlerContextAccessor _accessor = new();
private readonly DbContextOptions<AppDb> _options;
public ProductConcurrencyShould()
{
var databaseName = Guid.NewGuid().ToString();
_options = new DbContextOptionsBuilder<AppDb>()
.UseInMemoryDatabase(databaseName)
.Options;
}
[Fact]
public async Task ThrowWhenSavingWithStaleRowVersion()
{
var checker = new PermissiveUniqueChecker();
await using (var seedContext = CreateContext())
{
var product = await Product.CreateModel("Demo Model", "demo-model", "Demo", checker, CancellationToken.None);
product.SetBasePrice(10m);
product.RowVersion = Guid.NewGuid().ToByteArray();
seedContext.Products.Add(product);
await seedContext.SaveChangesAsync(CancellationToken.None);
}
await using var writerOne = CreateContext();
await using var writerTwo = CreateContext();
var first = await writerOne.Products.FirstAsync(CancellationToken.None);
var second = await writerTwo.Products.FirstAsync(CancellationToken.None);
first.SetBasePrice(12m);
writerOne.Entry(first).Property(p => p.RowVersion).CurrentValue = Guid.NewGuid().ToByteArray();
await writerOne.SaveChangesAsync(CancellationToken.None);
second.SetBasePrice(14m);
await Should.ThrowAsync<DbUpdateConcurrencyException>(
() => writerTwo.SaveChangesAsync(CancellationToken.None));
}
public ValueTask InitializeAsync() => ValueTask.CompletedTask;
private AppDb CreateContext() => new(_options, _accessor);
public void Dispose()
{
}
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
private sealed class PermissiveUniqueChecker : IUniqueChecker
{
public Task<bool> CategoryNameIsUnique(string name, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> ProductModelNameIsUnique(string name, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> ProductSlugIsUnique(string? slug, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> ProductSkuIsUnique(string sku, CancellationToken cancellationToken = default) => Task.FromResult(true);
public Task<bool> OptionCodeIsUniqueForProduct(Guid productId, string code, CancellationToken cancellationToken = default) => Task.FromResult(true);
}
}