From 1c2e1760b002f1607acda52081639c64f78de471 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Fri, 28 Aug 2026 21:33:11 +0100 Subject: [PATCH] refacto + test fixes --- .vscode/settings.json | 3 +- src/Yavsc.Blogs.Tests/BlogAclApiTests.cs | 3 +- .../BlogApiMappedClaimsTests.cs | 14 +++++-- src/Yavsc.Blogs.Tests/BlogApiTests.cs | 41 ++++++++----------- .../BlogsWebServerFixture.cs | 3 +- src/Yavsc.Blogs.Tests/Fixtures/BlogHelpers.cs | 11 +++++ .../Fixtures/IBackendFixture.cs | 15 +++++++ src/Yavsc.Blogs/Constants.cs | 2 +- 8 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 src/Yavsc.Blogs.Tests/Fixtures/BlogHelpers.cs create mode 100644 src/Yavsc.Blogs.Tests/Fixtures/IBackendFixture.cs diff --git a/.vscode/settings.json b/.vscode/settings.json index 915683fc..1cb334e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,5 +42,6 @@ "copilotcli/gpt-5.3-codex" ] } - } + }, + "dotnet.defaultSolution": "yavsc.sln" } diff --git a/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs b/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs index c140c290..4aef805f 100644 --- a/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs +++ b/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs @@ -131,7 +131,8 @@ public sealed class BlogAclApiTests : IClassFixture BlogPostId = _fixture.PostId }; - var response = await http.PostAsJsonAsync(BlogAclUrl(), payload, + var response = await http.PostAsJsonAsync( + BlogAclUrl(), payload, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.Created, response.StatusCode); diff --git a/src/Yavsc.Blogs.Tests/BlogApiMappedClaimsTests.cs b/src/Yavsc.Blogs.Tests/BlogApiMappedClaimsTests.cs index f4878860..7c5747d0 100644 --- a/src/Yavsc.Blogs.Tests/BlogApiMappedClaimsTests.cs +++ b/src/Yavsc.Blogs.Tests/BlogApiMappedClaimsTests.cs @@ -12,10 +12,16 @@ using Yavsc.Tests.Shared; namespace Yavsc.Blogs.Tests; [Collection("JwtClaimMapping")] -public sealed class BlogApiMappedClaimsTests : IClassFixture +public sealed class BlogApiMappedClaimsTests : +IClassFixture, +IBackendFixture { private readonly MappedClaimsBlogsWebServerFixture _fixture; + public IReadOnlyList Addresses => throw new NotImplementedException(); + + public IServiceProvider Services => throw new NotImplementedException(); + public BlogApiMappedClaimsTests(MappedClaimsBlogsWebServerFixture fixture) { _fixture = fixture; @@ -79,7 +85,7 @@ public sealed class BlogApiMappedClaimsTests : IClassFixture(TestContext.Current.CancellationToken); @@ -93,7 +99,7 @@ public sealed class BlogApiMappedClaimsTests : IClassFixture _fixture.SeedUser("tester"); } - /// The fixture's WebApplication is bound to - /// https://localhost:<random> via - /// . We pick the first - /// https URL and append the controller route - /// (/api/v1/blog, matching the production - /// [Route(APIPrefix + "/blog")]). - private string BlogsUrl => - _fixture.Addresses.First(a => a.StartsWith("https://")) + "/api/v1/blog"; - + /// Build an authenticated client: a real /// Authorization: Bearer <jwt> header where the JWT /// is signed by and carries @@ -101,7 +93,8 @@ public sealed class BlogApiTests : IClassFixture _fixture.ResetDatabase(); using var http = NewClient(); - var response = await http.GetAsync("/api/v1/blog", + var response = await http.GetAsync( + _fixture.BlogUrl(), TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -134,7 +127,7 @@ public sealed class BlogApiTests : IClassFixture DateModified = DateTime.UtcNow }; - var postResponse = await http.PostAsJsonAsync("/api/v1/blog", draft, + var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); @@ -147,7 +140,7 @@ public sealed class BlogApiTests : IClassFixture Assert.Equal(draft.Title, created.Title); // The list should now contain exactly one entry. - var listResponse = await http.GetAsync("/api/v1/blog", + var listResponse = await http.GetAsync(_fixture.BlogUrl(), TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); @@ -175,7 +168,7 @@ public sealed class BlogApiTests : IClassFixture DateModified = DateTime.UtcNow }; - var postResponse = await http.PostAsJsonAsync("/api/v1/blog", draft, + var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); @@ -185,7 +178,7 @@ public sealed class BlogApiTests : IClassFixture Assert.NotNull(created); Assert.Equal("tester", created!.AuthorId); - var listResponse = await http.GetAsync("/api/v1/blog", + var listResponse = await http.GetAsync(_fixture.BlogUrl(), TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); @@ -213,7 +206,7 @@ public sealed class BlogApiTests : IClassFixture DateModified = DateTime.UtcNow }; - var postResponse = await http.PostAsJsonAsync("/api/v1/blog", draft, + var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); @@ -262,7 +255,7 @@ public sealed class BlogApiTests : IClassFixture // the framework returns 401. This is the proof that the // production policy is wired in the test host and not // short-circuited by a test-only auth bypass. - var response = await http.GetAsync("/api/v1/blog", + var response = await http.GetAsync(_fixture.BlogUrl(), TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } @@ -290,7 +283,7 @@ public sealed class BlogApiTests : IClassFixture DateCreated = DateTime.UtcNow, DateModified = DateTime.UtcNow }; - var postResponse = await http.PostAsJsonAsync("/api/v1/blog", draft, + var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); @@ -309,13 +302,13 @@ public sealed class BlogApiTests : IClassFixture DateCreated = created.DateCreated, DateModified = DateTime.UtcNow }; - var putResponse = await http.PutAsJsonAsync($"/api/v1/blog/{created.Id}", + var putResponse = await http.PutAsJsonAsync(_fixture.BlogUrl()+$"/{created.Id}", update, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.NoContent, putResponse.StatusCode); // The list should now reflect the new title. - var listResponse = await http.GetAsync("/api/v1/blog", + var listResponse = await http.GetAsync(_fixture.BlogUrl(), TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); using var doc = JsonDocument.Parse( @@ -343,19 +336,19 @@ public sealed class BlogApiTests : IClassFixture DateCreated = DateTime.UtcNow, DateModified = DateTime.UtcNow }; - var postResponse = await http.PostAsJsonAsync("/api/v1/blog", draft, + var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); var created = (await postResponse.Content.ReadFromJsonAsync( TestContext.Current.CancellationToken ))!; - var deleteResponse = await http.DeleteAsync($"/api/v1/blog/{created.Id}", + var deleteResponse = await http.DeleteAsync(_fixture.BlogUrl()+$"/{created.Id}", TestContext.Current.CancellationToken ); Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode); // The list should now be empty. - var listResponse = await http.GetAsync("/api/v1/blog", + var listResponse = await http.GetAsync(_fixture.BlogUrl(), TestContext.Current.CancellationToken); String response = await listResponse.Content.ReadAsStringAsync( TestContext.Current.CancellationToken @@ -398,7 +391,7 @@ public sealed class BlogApiTests : IClassFixture DateModified = DateTime.UtcNow }; - var response = await http.PostAsJsonAsync("/api/v1/blog", draft, + var response = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); // Dump the body on failure so the test name + the response @@ -443,7 +436,7 @@ public sealed class BlogApiTests : IClassFixture DateModified = DateTime.UtcNow }; - var response = await http.PostAsJsonAsync("/api/v1/blog", draft, + var response = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); if (response.StatusCode != HttpStatusCode.BadRequest) diff --git a/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs b/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs index 695af4c5..6e8ae31f 100644 --- a/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs +++ b/src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs @@ -10,7 +10,7 @@ using Yavsc.Models.Blog; using Yavsc.Models.Relationship; using Yavsc.Services; using Yavsc.Tests.Shared; - +using static Yavsc.Constants; namespace Yavsc.Blogs.Tests; /// @@ -415,4 +415,5 @@ public sealed class BlogsWebServerFixture : WebHostFixture db.SaveChanges(); return post.Id; } + } diff --git a/src/Yavsc.Blogs.Tests/Fixtures/BlogHelpers.cs b/src/Yavsc.Blogs.Tests/Fixtures/BlogHelpers.cs new file mode 100644 index 00000000..06e9a5af --- /dev/null +++ b/src/Yavsc.Blogs.Tests/Fixtures/BlogHelpers.cs @@ -0,0 +1,11 @@ +namespace Yavsc.Blogs.Tests.Fixtures; + + +public static class BlogHelpers +{ + public static string BlogUrl(this IBackendFixture fixture) + => $"{fixture.Addresses.First(a => a.StartsWith("https://"))}/{Constants.APIPrefix}/{Constants.BlogSpotPath}"; + + public static string BlogAclUrl(this IBackendFixture fixture) + => $"{fixture.Addresses.First(a => a.StartsWith("https://"))}/{Constants.APIPrefix}/{Constants.BlogAclPath}"; +} \ No newline at end of file diff --git a/src/Yavsc.Blogs.Tests/Fixtures/IBackendFixture.cs b/src/Yavsc.Blogs.Tests/Fixtures/IBackendFixture.cs new file mode 100644 index 00000000..61202a0a --- /dev/null +++ b/src/Yavsc.Blogs.Tests/Fixtures/IBackendFixture.cs @@ -0,0 +1,15 @@ + +using Yavsc.Blogs.Tests; + +public interface IBackendFixture +{ + /// + /// The addresses the fixture bound to. + /// + IReadOnlyList Addresses { get; } + + /// + /// The service provider for the fixture host. + /// + IServiceProvider Services { get; } +} \ No newline at end of file diff --git a/src/Yavsc.Blogs/Constants.cs b/src/Yavsc.Blogs/Constants.cs index 3e499da4..9d400032 100644 --- a/src/Yavsc.Blogs/Constants.cs +++ b/src/Yavsc.Blogs/Constants.cs @@ -1,6 +1,6 @@ namespace Yavsc.Blogs; -public static class Constants +public static class BlogConstants { public const string AdminRole = "Admin"; public const string ModeratorRole = "Moderator";