feat(postit): circles+ACL UI, blog fixture→SQLite, seed default user
Bundled end-of-branch commit on feat/postit-acl-members. PostIt UI for circles + per-post ACL - Reorganise PostIt.Tests into Auth/ and Blogs/ subfolders (Bearer/OIDC scope tests vs. blog API fakes live where they belong) and introduces PostItHeadlessCollection so the Avalonia.Headless tests share a single xUnit collection instead of contending with the EF-Core test host. - Adds BlogAclApiTests (a brand-new behavioural layer over POST /api/v1/blogacl) and the fakes it relies on (BlogApiTestFakes, BlogPostAuthorDtoTests, AddCircleMember DialogTests); pulls UserId-through-OIDC-sub path into BearerScopeTests / FakeAuthorizingBrowser / OidcStubAuthority. - App.axaml.cs gets a small PushPageAsync touch-up the new tests rely on. - Drops UnitTest1.cs (xUnit scaffold, never used). Yavsc.Blogs.Tests — SQLite instead of InMemory - Bumps Yavsc.Blogs.Tests.csproj on Microsoft.EntityFrameworkCore.Sqlite and rewrites BlogsWebServerFixture to hold a single shared SqliteConnection (Cache=Shared) for the fixture lifetime, with a sync Dispose close to dodge async teardown hangs. Reason: the EF Core InMemory provider silently ignores FKs, which masked the kind of bug we are about to pin in the ACL tests. SQLite enforces them, so any future INSERT that forgets to seed its parent rows fails loudly here instead of passing the test and breaking prod. - PublishEndpointTests and BlogApiSmokeTests get a one-line tweak to follow the new connection lifecycle. Foreign-key fallout: seed the default user in the fixture - Adds BlogsWebServerFixture.SeedUser(userName). Now that SQLite enforces BlogPost.AuthorId → AspNetUsers.Id, every test that POST/PUT/DELETE a BlogPost and sends AuthorId= 'tester' in the payload needs an AspNetUsers row to satisfy the FK or it returns 500 with SQLite Error 19. - BlogApiTests wraps the existing ResetDatabase with a ResetAndSeedDefaultUser helper for the six mutating tests; the four GET-only and ModelState-only tests keep the bare ResetDatabase. - Side benefit: every test in Yavsc.Blogs.Tests now finishes cleanly instead of hanging at teardown — previously a stuck test held the shared SqliteConnection open and the next tests waited indefinitely. Verified: dotnet test src/Yavsc.Blogs.Tests passes 25/25 green from a clean run, no fixture teardown hang.
This commit is contained in:
parent
6825f74308
commit
a44c04ad77
22 changed files with 806 additions and 460 deletions
|
|
@ -22,7 +22,7 @@ namespace Yavsc.Blogs.Tests;
|
|||
/// header (or sending a token signed with the wrong key) gets a
|
||||
/// 401 back from the framework.
|
||||
/// </summary>
|
||||
[Collection("JwtClaimMapping")]
|
||||
[Collection("Yavsc Blogs")]
|
||||
public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
||||
{
|
||||
private readonly BlogsWebServerFixture _fixture;
|
||||
|
|
@ -45,6 +45,21 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
db.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
/// <summary>Reset the database and seed the
|
||||
/// <c>tester</c> <see cref="ApplicationUser"/> row. Required
|
||||
/// for any test that POST/PUT/DELETE a <c>BlogPost</c>:
|
||||
/// <c>BlogPost.AuthorId</c> is a FK to
|
||||
/// <c>AspNetUsers.Id</c>, and SQLite (unlike the EF Core
|
||||
/// InMemory provider) enforces it. Without the seed, the
|
||||
/// POST handler hits
|
||||
/// <c>SQLite Error 19: 'FOREIGN KEY constraint failed'</c>
|
||||
/// at <c>SaveChanges</c> and the controller returns 500.</summary>
|
||||
private void ResetAndSeedDefaultUser()
|
||||
{
|
||||
ResetDatabase();
|
||||
_fixture.SeedUser("tester");
|
||||
}
|
||||
|
||||
/// <summary>The fixture's <c>WebApplication</c> is bound to
|
||||
/// <c>https://localhost:<random></c> via
|
||||
/// <see cref="WebHostFixture.Addresses"/>. We pick the first
|
||||
|
|
@ -116,7 +131,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
[Fact]
|
||||
public async Task PostBlog_creates_a_post_and_Get_returns_it_in_the_list()
|
||||
{
|
||||
ResetDatabase();
|
||||
ResetAndSeedDefaultUser();
|
||||
using var http = NewClient();
|
||||
|
||||
// Create a minimal BlogPost. The server assigns Id, so we
|
||||
|
|
@ -154,7 +169,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
[Fact]
|
||||
public async Task PostBlog_sets_AuthorId_on_created_post_and_list_entry()
|
||||
{
|
||||
ResetDatabase();
|
||||
ResetAndSeedDefaultUser();
|
||||
using var http = NewClient(subject: "tester");
|
||||
|
||||
var draft = new BlogPost
|
||||
|
|
@ -186,7 +201,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
[Fact]
|
||||
public async Task PostBlogComment_returns_201_for_existing_post()
|
||||
{
|
||||
ResetDatabase();
|
||||
ResetAndSeedDefaultUser();
|
||||
using var http = NewClient(subject: "tester");
|
||||
|
||||
var draft = new BlogPost
|
||||
|
|
@ -249,7 +264,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
[Fact]
|
||||
public async Task PutBlog_with_valid_token_and_owner_returns_204_and_Get_reflects_update()
|
||||
{
|
||||
ResetDatabase();
|
||||
ResetAndSeedDefaultUser();
|
||||
// The JWT's sub must match the post's AuthorId:
|
||||
// PermissionHandler.IsOwner checks blog.AuthorId == user.GetUserId(),
|
||||
// and UserHelpers.GetUserId reads "sub" off the principal.
|
||||
|
|
@ -300,7 +315,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
[Fact]
|
||||
public async Task DeleteBlog_removes_a_post_and_Get_returns_an_empty_list()
|
||||
{
|
||||
ResetDatabase();
|
||||
ResetAndSeedDefaultUser();
|
||||
using var http = NewClient();
|
||||
|
||||
// Seed a post we can delete.
|
||||
|
|
@ -342,7 +357,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
// ModelState validation starts rejecting the PostIt payload
|
||||
// (missing field, wrong casing, etc.), this test fails
|
||||
// before the regression reaches a user.
|
||||
ResetDatabase();
|
||||
ResetAndSeedDefaultUser();
|
||||
using var http = NewClient(subject: "tester");
|
||||
|
||||
// Mirrors what MainPageViewModel.Save builds: a BlogPost with
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue