Files
jobtrackingapp/JobTrackerApi.MariaDb.Tests/DashboardGeometryPersistenceTests.cs
T

66 lines
3.6 KiB
C#

using System.Security.Claims;
using System.Text;
using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Xunit;
namespace JobTrackerApi.MariaDb.Tests;
public sealed class DashboardGeometryPersistenceTests
{
private const string Geometry = "{\"version\":2,\"revision\":0,\"widgets\":[{\"id\":\"attention\",\"visible\":false,\"size\":\"standard\",\"x\":4,\"y\":17,\"w\":8,\"h\":10}]}";
private static AuthController Controller(JobTrackerContext db, string body, string owner = "rd21-owner")
{
var http = new DefaultHttpContext { User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, owner) }, "test")) };
http.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));
return new(new ConfigurationBuilder().Build(), null!, null!, null!, null!, null!, null!, null!, db)
{ ControllerContext = new() { HttpContext = http } };
}
[Fact]
public async Task Geometry_is_durable_owner_scoped_and_stale_or_legacy_writes_preserve_other_preferences()
{
await using var database = await MariaDbDatabase.CreateAsync();
await using (var seed = database.Context())
{
seed.Users.AddRange(new ApplicationUser { Id = "rd21-owner", UserName = "geometry", UiLanguage = "nb-NO" }, new ApplicationUser { Id = "other", UserName = "untouched" });
await seed.SaveChangesAsync();
}
await using (var write = database.Context())
Assert.IsType<OkObjectResult>(await Controller(write, Geometry).UpdateDashboardPreference(default));
await using var verify = database.Context();
var owner = await verify.Users.AsNoTracking().SingleAsync(u => u.Id == "rd21-owner");
var saved = DashboardLayout.Read(owner.DashboardLayoutJson, owner.DashboardLayoutRevision);
Assert.Equal(1, saved.Revision);
Assert.Equal(new DashboardWidget("attention", false, "standard", 4, 17, 8, 10), Assert.Single(saved.Widgets));
Assert.Equal("nb-NO", owner.UiLanguage);
Assert.Null((await verify.Users.AsNoTracking().SingleAsync(u => u.Id == "other")).DashboardLayoutJson);
Assert.IsType<ConflictObjectResult>(await Controller(verify, Geometry).UpdateDashboardPreference(default));
Assert.IsType<ConflictObjectResult>(await Controller(verify, "{\"version\":1,\"revision\":1,\"widgets\":[]}").UpdateDashboardPreference(default));
Assert.Equal(owner.DashboardLayoutJson, (await verify.Users.AsNoTracking().SingleAsync(u => u.Id == owner.Id)).DashboardLayoutJson);
}
[Fact]
public async Task Concurrent_geometry_saves_have_one_winner_and_one_conflict()
{
await using var database = await MariaDbDatabase.CreateAsync();
await using (var seed = database.Context())
{
seed.Users.Add(new() { Id = "rd21-owner", UserName = "concurrent-geometry" });
await seed.SaveChangesAsync();
}
await using var first = database.Context();
await using var second = database.Context();
var results = await Task.WhenAll(Controller(first, Geometry).UpdateDashboardPreference(default), Controller(second, Geometry.Replace("\"h\":10", "\"h\":12")).UpdateDashboardPreference(default));
Assert.Single(results.OfType<OkObjectResult>());
Assert.Single(results.OfType<ConflictObjectResult>());
await using var readback = database.Context();
Assert.Equal(1, (await readback.Users.SingleAsync()).DashboardLayoutRevision);
}
}