test(rules): verify failed save preserves committed owner settings

This commit is contained in:
cesnimda
2026-11-11 12:46:30 +01:00
parent beb5564223
commit 361e79ddf3
@@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Moq;
using Xunit;
@@ -15,6 +16,43 @@ namespace JobTrackerApi.Tests;
public sealed class RuleSettingsFreshnessTests
{
[Fact]
public async Task Failed_owner_save_preserves_committed_rules_and_a_later_success_refreshes_existing_readers()
{
await using var fixture = await Fixture.CreateAsync();
await using var readA = fixture.Context("owner-a");
await using var readB = fixture.Context("owner-b");
var ownerA = fixture.Controller(readA);
var ownerB = fixture.Controller(readB);
Assert.False(await FollowUp(ownerA, fixture.JobA, "list"));
Assert.False(await FollowUp(ownerB, fixture.JobB, "list"));
var failure = new SaveFailureInterceptor();
await using var write = fixture.Context("owner-a", failure);
await Assert.ThrowsAsync<DbUpdateException>(() => new RulesController(write).Update(Settings(1), default));
Assert.Equal(1, write.UserRuleSettings.Local.Single().AppliedFollowUpDays);
Assert.Equal(30, (await RulesEngine.GetSettings(write, default)).AppliedFollowUpDays);
Assert.False(await FollowUp(ownerA, fixture.JobA, "list"));
Assert.False(await FollowUp(ownerB, fixture.JobB, "list"));
failure.RejectWrites = false;
Assert.IsType<NoContentResult>(await new RulesController(write).Update(Settings(1), default));
Assert.True(await FollowUp(ownerA, fixture.JobA, "list"));
Assert.False(await FollowUp(ownerB, fixture.JobB, "list"));
}
private sealed class SaveFailureInterceptor : SaveChangesInterceptor
{
public bool RejectWrites { get; set; } = true;
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
if (RejectWrites) throw new DbUpdateException("Synthetic settings storage failure");
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
}
[Theory]
[InlineData("list")]
[InlineData("board")]
@@ -158,11 +196,13 @@ public sealed class RuleSettingsFreshnessTests
public int JobA { get; private set; }
public int JobB { get; private set; }
public JobTrackerContext Context(string? owner)
public JobTrackerContext Context(string? owner, IInterceptor? interceptor = null)
{
var user = new Mock<ICurrentUserService>();
user.SetupGet(x => x.UserId).Returns(owner);
return new JobTrackerContext(new DbContextOptionsBuilder<JobTrackerContext>().UseSqlite(_connection).Options, user.Object);
var options = new DbContextOptionsBuilder<JobTrackerContext>().UseSqlite(_connection);
if (interceptor is not null) options.AddInterceptors(interceptor);
return new JobTrackerContext(options.Options, user.Object);
}
public JobApplicationsController Controller(JobTrackerContext db) =>