fixes the tests
All checks were successful
Dotnet build and test / build (pull_request) Successful in 7m35s

This commit is contained in:
Paul Schneider 2026-08-28 22:41:05 +01:00
commit e689dbfe35
Signed by: notazof
GPG key ID: 1DD5D838E5343B06
11 changed files with 79 additions and 55 deletions

View file

@ -8,6 +8,7 @@ using Microsoft.IdentityModel.Tokens;
using Yavsc.Models; using Yavsc.Models;
using Yavsc.Models.Blog; using Yavsc.Models.Blog;
using Yavsc.Tests.Shared; using Yavsc.Tests.Shared;
using Yavsc.Blogs.Tests.Fixtures;
namespace Yavsc.Blogs.Tests; namespace Yavsc.Blogs.Tests;
@ -85,7 +86,10 @@ IBackendFixture
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var response = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, TestContext.Current.CancellationToken); var response = await http.PostAsJsonAsync(
_fixture.BlogSpotUrl(),
draft,
TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.Equal(HttpStatusCode.Created, response.StatusCode);
var created = await response.Content.ReadFromJsonAsync<BlogPost>(TestContext.Current.CancellationToken); var created = await response.Content.ReadFromJsonAsync<BlogPost>(TestContext.Current.CancellationToken);
@ -99,7 +103,7 @@ IBackendFixture
ResetDatabase(); ResetDatabase();
using var http = NewClient(subject: "mapped-owner"); using var http = NewClient(subject: "mapped-owner");
var createdResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), new BlogPost var createdResponse = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), new BlogPost
{ {
Id = 0, Id = 0,
Title = "Billet à modifier", Title = "Billet à modifier",
@ -113,7 +117,7 @@ IBackendFixture
var created = await createdResponse.Content.ReadFromJsonAsync<BlogPost>(TestContext.Current.CancellationToken); var created = await createdResponse.Content.ReadFromJsonAsync<BlogPost>(TestContext.Current.CancellationToken);
Assert.NotNull(created); Assert.NotNull(created);
var updateResponse = await http.PutAsJsonAsync($"/api/v1/blog/{created!.Id}", new BlogPost var updateResponse = await http.PutAsJsonAsync(_fixture.BlogSpotUrl() + $"/{created!.Id}", new BlogPost
{ {
Id = created.Id, Id = created.Id,
Title = "Billet modifié", Title = "Billet modifié",
@ -132,7 +136,7 @@ IBackendFixture
ResetDatabase(); ResetDatabase();
using var ownerHttp = NewClient(subject: "mapped-owner"); using var ownerHttp = NewClient(subject: "mapped-owner");
var createdResponse = await ownerHttp.PostAsJsonAsync(_fixture.BlogUrl(), new BlogPost var createdResponse = await ownerHttp.PostAsJsonAsync(_fixture.BlogSpotUrl(), new BlogPost
{ {
Id = 0, Id = 0,
Title = "Billet protégé", Title = "Billet protégé",
@ -147,7 +151,7 @@ IBackendFixture
Assert.NotNull(created); Assert.NotNull(created);
using var otherHttp = NewClient(subject: "mapped-other"); using var otherHttp = NewClient(subject: "mapped-other");
var updateResponse = await otherHttp.PutAsJsonAsync($"/api/v1/blog/{created!.Id}", new BlogPost var updateResponse = await otherHttp.PutAsJsonAsync(_fixture.BlogSpotUrl() + $"/{created!.Id}", new BlogPost
{ {
Id = created.Id, Id = created.Id,
Title = "Tentative de modification", Title = "Tentative de modification",

View file

@ -6,6 +6,7 @@ using Yavsc.Models;
using Yavsc.Models.Blog; using Yavsc.Models.Blog;
using Yavsc.Server.Helpers; using Yavsc.Server.Helpers;
using Yavsc.Tests.Shared; using Yavsc.Tests.Shared;
using Yavsc.Blogs.Tests.Fixtures;
namespace Yavsc.Blogs.Tests; namespace Yavsc.Blogs.Tests;
@ -94,7 +95,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
using var http = NewClient(); using var http = NewClient();
var response = await http.GetAsync( var response = await http.GetAsync(
_fixture.BlogUrl(), _fixture.BlogSpotUrl(),
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -127,7 +128,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var postResponse = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode);
@ -140,7 +141,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
Assert.Equal(draft.Title, created.Title); Assert.Equal(draft.Title, created.Title);
// The list should now contain exactly one entry. // The list should now contain exactly one entry.
var listResponse = await http.GetAsync(_fixture.BlogUrl(), var listResponse = await http.GetAsync(_fixture.BlogSpotUrl(),
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
@ -168,7 +169,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var postResponse = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode);
@ -178,7 +179,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
Assert.NotNull(created); Assert.NotNull(created);
Assert.Equal("tester", created!.AuthorId); Assert.Equal("tester", created!.AuthorId);
var listResponse = await http.GetAsync(_fixture.BlogUrl(), var listResponse = await http.GetAsync(_fixture.BlogSpotUrl(),
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
@ -206,7 +207,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var postResponse = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode);
@ -255,7 +256,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
// the framework returns 401. This is the proof that the // the framework returns 401. This is the proof that the
// production policy is wired in the test host and not // production policy is wired in the test host and not
// short-circuited by a test-only auth bypass. // short-circuited by a test-only auth bypass.
var response = await http.GetAsync(_fixture.BlogUrl(), var response = await http.GetAsync(_fixture.BlogSpotUrl(),
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
} }
@ -283,7 +284,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateCreated = DateTime.UtcNow, DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var postResponse = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode); Assert.Equal(HttpStatusCode.Created, postResponse.StatusCode);
@ -302,13 +303,13 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateCreated = created.DateCreated, DateCreated = created.DateCreated,
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var putResponse = await http.PutAsJsonAsync(_fixture.BlogUrl()+$"/{created.Id}", var putResponse = await http.PutAsJsonAsync(_fixture.BlogSpotUrl()+$"/{created.Id}",
update, update,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.NoContent, putResponse.StatusCode); Assert.Equal(HttpStatusCode.NoContent, putResponse.StatusCode);
// The list should now reflect the new title. // The list should now reflect the new title.
var listResponse = await http.GetAsync(_fixture.BlogUrl(), var listResponse = await http.GetAsync(_fixture.BlogSpotUrl(),
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
using var doc = JsonDocument.Parse( using var doc = JsonDocument.Parse(
@ -336,19 +337,19 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateCreated = DateTime.UtcNow, DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var postResponse = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var postResponse = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
var created = (await postResponse.Content.ReadFromJsonAsync<BlogPost>( var created = (await postResponse.Content.ReadFromJsonAsync<BlogPost>(
TestContext.Current.CancellationToken TestContext.Current.CancellationToken
))!; ))!;
var deleteResponse = await http.DeleteAsync(_fixture.BlogUrl()+$"/{created.Id}", var deleteResponse = await http.DeleteAsync(_fixture.BlogSpotUrl()+$"/{created.Id}",
TestContext.Current.CancellationToken TestContext.Current.CancellationToken
); );
Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode);
// The list should now be empty. // The list should now be empty.
var listResponse = await http.GetAsync(_fixture.BlogUrl(), var listResponse = await http.GetAsync(_fixture.BlogSpotUrl(),
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
String response = await listResponse.Content.ReadAsStringAsync( String response = await listResponse.Content.ReadAsStringAsync(
TestContext.Current.CancellationToken TestContext.Current.CancellationToken
@ -391,7 +392,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var response = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var response = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
// Dump the body on failure so the test name + the response // Dump the body on failure so the test name + the response
@ -436,7 +437,7 @@ public sealed class BlogApiTests : IClassFixture<BlogsWebServerFixture>
DateModified = DateTime.UtcNow DateModified = DateTime.UtcNow
}; };
var response = await http.PostAsJsonAsync(_fixture.BlogUrl(), draft, var response = await http.PostAsJsonAsync(_fixture.BlogSpotUrl(), draft,
TestContext.Current.CancellationToken); TestContext.Current.CancellationToken);
if (response.StatusCode != HttpStatusCode.BadRequest) if (response.StatusCode != HttpStatusCode.BadRequest)

View file

@ -1,11 +0,0 @@
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

@ -21,7 +21,7 @@ namespace Yavsc.Blogs.Tests;
/// This is the closest in-process reproduction of the production /// This is the closest in-process reproduction of the production
/// authentication surface for the blog API. /// authentication surface for the blog API.
/// </summary> /// </summary>
public sealed class MappedClaimsBlogsWebServerFixture : IDisposable public sealed class MappedClaimsBlogsWebServerFixture : IDisposable, IBackendFixture
{ {
private readonly InMemoryDatabaseRoot _inMemoryRoot = new(); private readonly InMemoryDatabaseRoot _inMemoryRoot = new();
private readonly Dictionary<string, string> _savedInboundMap; private readonly Dictionary<string, string> _savedInboundMap;

View file

@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
using Yavsc.Models; using Yavsc.Models;
using Yavsc.Models.Blog; using Yavsc.Models.Blog;
using Yavsc.Tests.Shared; using Yavsc.Tests.Shared;
using Yavsc.Blogs.Tests.Fixtures;
namespace Yavsc.Blogs.Tests; namespace Yavsc.Blogs.Tests;
@ -71,12 +72,6 @@ public sealed class PublishEndpointTests : IClassFixture<BlogsWebServerFixture>
return post.Id; return post.Id;
} }
private string PublishUrl(long id)
=> $"{_fixture.Addresses.First(a => a.StartsWith("https://"))}/api/v1/blog/{id}/publish";
private string BlogsUrl
=> _fixture.Addresses.First(a => a.StartsWith("https://")) + "/api/v1/blog";
private HttpClient NewClient(string subject) private HttpClient NewClient(string subject)
{ {
var handler = new HttpClientHandler var handler = new HttpClientHandler
@ -100,12 +95,13 @@ public sealed class PublishEndpointTests : IClassFixture<BlogsWebServerFixture>
var postId = SeedPost("alice"); var postId = SeedPost("alice");
using var http = NewClient("alice"); using var http = NewClient("alice");
var put = await http.PutAsJsonAsync(PublishUrl(postId), new { publish = true }, TestContext.Current.CancellationToken); var put = await http.PutAsJsonAsync(_fixture.PublishUrl(postId), new { publish = true }, TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.NoContent, put.StatusCode); Assert.Equal(HttpStatusCode.NoContent, put.StatusCode);
var get = await http.GetAsync($"{BlogsUrl}/{postId}", TestContext.Current.CancellationToken); var get = await http.GetAsync(_fixture.BlogSpotUrl() + $"/{postId}", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, get.StatusCode); Assert.Equal(HttpStatusCode.OK, get.StatusCode);
using var doc = JsonDocument.Parse(await get.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); using var doc = JsonDocument.Parse(await get.Content.ReadAsStringAsync(TestContext.Current.CancellationToken));
Assert.Equal($"post-by-alice", doc.RootElement.GetProperty("title").GetString());
Assert.True(doc.RootElement.GetProperty("isPublished").GetBoolean()); Assert.True(doc.RootElement.GetProperty("isPublished").GetBoolean());
} }
@ -116,11 +112,12 @@ public sealed class PublishEndpointTests : IClassFixture<BlogsWebServerFixture>
var postId = SeedPost("alice"); var postId = SeedPost("alice");
using var http = NewClient("alice"); using var http = NewClient("alice");
await http.PutAsJsonAsync(PublishUrl(postId), new { publish = true }, TestContext.Current.CancellationToken); await http.PutAsJsonAsync(_fixture.PublishUrl(postId), new { publish = true }, TestContext.Current.CancellationToken);
var put = await http.PutAsJsonAsync(PublishUrl(postId), new { publish = false }, TestContext.Current.CancellationToken); var put = await http.PutAsJsonAsync(_fixture.PublishUrl(postId), new { publish = false }, TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.NoContent, put.StatusCode); Assert.Equal(HttpStatusCode.NoContent, put.StatusCode);
var get = await http.GetAsync($"{BlogsUrl}/{postId}", TestContext.Current.CancellationToken); var get = await http.GetAsync(_fixture.BlogSpotUrl() + $"/{postId}", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, get.StatusCode);
using var doc = JsonDocument.Parse(await get.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); using var doc = JsonDocument.Parse(await get.Content.ReadAsStringAsync(TestContext.Current.CancellationToken));
Assert.False(doc.RootElement.GetProperty("isPublished").GetBoolean()); Assert.False(doc.RootElement.GetProperty("isPublished").GetBoolean());
} }
@ -130,7 +127,7 @@ public sealed class PublishEndpointTests : IClassFixture<BlogsWebServerFixture>
{ {
ResetDatabase(); ResetDatabase();
using var http = NewClient("alice"); using var http = NewClient("alice");
var put = await http.PutAsJsonAsync(PublishUrl(99999L), new { publish = true }, TestContext.Current.CancellationToken); var put = await http.PutAsJsonAsync(_fixture.PublishUrl(99999L), new { publish = true }, TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.NotFound, put.StatusCode); Assert.Equal(HttpStatusCode.NotFound, put.StatusCode);
} }
@ -141,7 +138,7 @@ public sealed class PublishEndpointTests : IClassFixture<BlogsWebServerFixture>
var postId = SeedPost("alice"); var postId = SeedPost("alice");
using var http = NewClient("bob"); using var http = NewClient("bob");
var put = await http.PutAsJsonAsync(PublishUrl(postId), new { publish = true }, TestContext.Current.CancellationToken); var put = await http.PutAsJsonAsync(_fixture.PublishUrl(postId), new { publish = true }, TestContext.Current.CancellationToken);
// 401 Challenge (the controller returns Challenge() // 401 Challenge (the controller returns Challenge()
// for AuthorizationFailureException). The exact code // for AuthorizationFailureException). The exact code
// is framework-dependent; what matters is "not 204". // is framework-dependent; what matters is "not 204".

View file

@ -15,7 +15,8 @@ public static class PayloadHelpers
post.UserModified, post.UserModified,
post.AuthorId, post.AuthorId,
ACL = post.GetACL(), ACL = post.GetACL(),
Tags = post.GetTags() Tags = post.GetTags(),
post.IsPublished
}; };
} }
} }

View file

@ -0,0 +1,31 @@
namespace Yavsc.Blogs.Tests.Fixtures;
using static Yavsc.Constants;
public static class BlogHelpers
{
public static string ApiUrl(this IBackendFixture fixture, string apiSubPath)
{
var secured = fixture.Addresses.FirstOrDefault(a => a.StartsWith("https://"));
if (secured is null)
{
var unsecured = fixture.Addresses.FirstOrDefault(a => a.StartsWith("http://"));
if (unsecured is null)
{
throw new InvalidOperationException("No backend address found");
}
return $"{unsecured}/{APIPrefix}/{apiSubPath}";
}
return $"{secured}/{APIPrefix}/{apiSubPath}";
}
public static string BlogAclUrl(this IBackendFixture fixture)
=> fixture.ApiUrl(BlogAclPath);
public static string BlogSpotUrl(this IBackendFixture fixture)
=> fixture.ApiUrl(BlogSpotPath);
public static string PublishUrl(this IBackendFixture fixture, long id)
=> fixture.ApiUrl(BlogSpotPath) +"/" + id + "/publish";
}

View file

@ -1,6 +1,4 @@
using Yavsc.Blogs.Tests;
public interface IBackendFixture public interface IBackendFixture
{ {
/// <summary> /// <summary>

View file

@ -30,7 +30,7 @@ namespace Yavsc.Tests.Shared;
/// mapping are the responsibility of the subclass, through /// mapping are the responsibility of the subclass, through
/// <see cref="BuildApp"/>. /// <see cref="BuildApp"/>.
/// </summary> /// </summary>
public abstract class WebHostFixture : IDisposable public abstract class WebHostFixture : IDisposable, IBackendFixture
{ {
private static readonly Lazy<X509Certificate2> _selfSignedCertificate = private static readonly Lazy<X509Certificate2> _selfSignedCertificate =
new Lazy<X509Certificate2>(CreateSelfSignedCertificate); new Lazy<X509Certificate2>(CreateSelfSignedCertificate);

View file

@ -23,4 +23,7 @@
<PackageReference Include="Microsoft.IdentityModel.Tokens" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="../Yavsc.Abstract/Yavsc.Abstract.csproj" />
</ItemGroup>
</Project> </Project>