refacto + test fixes
Some checks failed
Dotnet build and test / build (pull_request) Failing after 5m11s

This commit is contained in:
Paul Schneider 2026-08-28 21:33:11 +01:00
commit 1c2e1760b0
Signed by: notazof
GPG key ID: 1DD5D838E5343B06
8 changed files with 60 additions and 32 deletions

View file

@ -42,5 +42,6 @@
"copilotcli/gpt-5.3-codex"
]
}
}
},
"dotnet.defaultSolution": "yavsc.sln"
}

View file

@ -131,7 +131,8 @@ public sealed class BlogAclApiTests : IClassFixture<BlogsWebServerFixture>
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);

View file

@ -12,10 +12,16 @@ using Yavsc.Tests.Shared;
namespace Yavsc.Blogs.Tests;
[Collection("JwtClaimMapping")]
public sealed class BlogApiMappedClaimsTests : IClassFixture<MappedClaimsBlogsWebServerFixture>
public sealed class BlogApiMappedClaimsTests :
IClassFixture<MappedClaimsBlogsWebServerFixture>,
IBackendFixture
{
private readonly MappedClaimsBlogsWebServerFixture _fixture;
public IReadOnlyList<string> 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<MappedClaimsBlogsWe
DateModified = DateTime.UtcNow
};
var response = await http.PostAsJsonAsync("/api/v1/blog", draft, TestContext.Current.CancellationToken);
var response = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
var created = await response.Content.ReadFromJsonAsync<BlogPost>(TestContext.Current.CancellationToken);
@ -93,7 +99,7 @@ public sealed class BlogApiMappedClaimsTests : IClassFixture<MappedClaimsBlogsWe
ResetDatabase();
using var http = NewClient(subject: "mapped-owner");
var createdResponse = await http.PostAsJsonAsync("/api/v1/blog", new BlogPost
var createdResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), new BlogPost
{
Id = 0,
Title = "Billet à modifier",
@ -126,7 +132,7 @@ public sealed class BlogApiMappedClaimsTests : IClassFixture<MappedClaimsBlogsWe
ResetDatabase();
using var ownerHttp = NewClient(subject: "mapped-owner");
var createdResponse = await ownerHttp.PostAsJsonAsync("/api/v1/blog", new BlogPost
var createdResponse = await ownerHttp.PostAsJsonAsync(_fixture.BlogUrl(), new BlogPost
{
Id = 0,
Title = "Billet protégé",

View file

@ -46,15 +46,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
_fixture.SeedUser("tester");
}
/// <summary>The fixture's <c>WebApplication</c> is bound to
/// <c>https://localhost:&lt;random&gt;</c> via
/// <see cref="WebHostFixture.Addresses"/>. We pick the first
/// https URL and append the controller route
/// (<c>/api/v1/blog</c>, matching the production
/// <c>[Route(APIPrefix + "/blog")]</c>).</summary>
private string BlogsUrl =>
_fixture.Addresses.First(a => a.StartsWith("https://")) + "/api/v1/blog";
/// <summary>Build an authenticated client: a real
/// <c>Authorization: Bearer &lt;jwt&gt;</c> header where the JWT
/// is signed by <see cref="TestTokenIssuer"/> and carries
@ -101,7 +93,8 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
_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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
// 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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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<BlogPost>(
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<BlogsWebServerFixture>
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<BlogsWebServerFixture>
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)

View file

@ -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;
/// <summary>
@ -415,4 +415,5 @@ public sealed class BlogsWebServerFixture : WebHostFixture
db.SaveChanges();
return post.Id;
}
}

View file

@ -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}";
}

View file

@ -0,0 +1,15 @@
using Yavsc.Blogs.Tests;
public interface IBackendFixture
{
/// <summary>
/// The addresses the fixture bound to.
/// </summary>
IReadOnlyList<string> Addresses { get; }
/// <summary>
/// The service provider for the fixture host.
/// </summary>
IServiceProvider Services { get; }
}

View file

@ -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";