shoc-backend/SeaHavenIndustries.Tests/UpliftDataServiceTransactionTests.cs

78 lines
2.8 KiB
C#
Raw Normal View History

using System.Data.Common;
using Data.SeaHavenIndustries;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using SeaHaven.DataServices.Implementation;
namespace SeaHavenIndustries.Tests;
public sealed class UpliftDataServiceTransactionTests
{
[Fact]
public async Task ExecuteWorkOrderMutationAsync_ReleasesGate_WhenTransactionInitializationFails()
{
await using var connection = new SqliteConnection("DataSource=:memory:");
await connection.OpenAsync();
var failingOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(connection)
.AddInterceptors(new ThrowOnceTransactionInterceptor())
.Options;
await using (var failedContext = new ApplicationDbContext(failingOptions))
{
var failedService = new UpliftDataService(failedContext);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
failedService.ExecuteWorkOrderMutationAsync(
387,
_ => Task.FromResult(true),
CancellationToken.None));
}
var retryOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(connection)
.Options;
await using var retryContext = new ApplicationDbContext(retryOptions);
var retryService = new UpliftDataService(retryContext);
var retry = retryService.ExecuteWorkOrderMutationAsync(
387,
_ => Task.FromResult("entered"),
CancellationToken.None);
Assert.Equal("entered", await retry.WaitAsync(TimeSpan.FromSeconds(1)));
}
private sealed class ThrowOnceTransactionInterceptor : DbTransactionInterceptor
{
private int _remaining = 1;
public override InterceptionResult<DbTransaction> TransactionStarting(
DbConnection connection,
TransactionStartingEventData eventData,
InterceptionResult<DbTransaction> result)
{
ThrowOnce();
return base.TransactionStarting(connection, eventData, result);
}
public override ValueTask<InterceptionResult<DbTransaction>> TransactionStartingAsync(
DbConnection connection,
TransactionStartingEventData eventData,
InterceptionResult<DbTransaction> result,
CancellationToken cancellationToken = default)
{
ThrowOnce();
return base.TransactionStartingAsync(connection, eventData, result, cancellationToken);
}
private void ThrowOnce()
{
if (Interlocked.Exchange(ref _remaining, 0) == 1)
throw new InvalidOperationException("Forced transaction initialization failure");
}
}
}