remove dead 'Comment' field from CircleAuthorizationToBlogPost
The bool Comment on CircleAuthorizationToBlogPost was dead code:
never read or written by any caller in src/, no UI exposure, no
behavioural semantics. The wire DTO (CircleAuthorization in
Yavsc.Abstract) doesn't carry it, no reader consumes it, and the
PostIt client builds its payload without it.
What changes:
- src/Yavsc.Server/Models/Access/CircleAuthorizationToBlogPost.cs:
remove the property.
- src/Yavsc.Blogs.Tests/BlogAclApiTests.cs: drop 'Comment = true'
from the existing test payload and trim the now-inaccurate XML
doc comment ('CircleId + BlogPostId + Comment' -> 'CircleId +
BlogPostId'). Also adds a new [Fact] pinning the prod bug
reported on 2026-08-21 (HTTP 500 'BlogPostId is unknown' when
PostIt POSTs the bare { circleId } shape). That test stays red:
the real fix for the 500 is in PostIt (payload needs blogPostId)
+ on the wire DTO + server-side validation, and lives in a
follow-up commit.
Migration:
- src/Yavsc.Org/Migrations/20260820232152_DropCommentFromCircleAuthorizationToBlogPost
drops the boolean 'Comment' column on CircleAuthorizationToBlogPost.
The generated scaffold also wanted to drop three 'ClientId1'
shadow FK columns on ClientScopes / ClientRedirectUris /
ClientGrantTypes (from leftover HasOne<Client>() overrides in
ApplicationDbContext.OnModelCreating); those were removed from
the .cs to keep the migration scoped to this fix. Cleaning up the
shadow property declarations themselves is left as a separate
task.
The ModelSnapshot still reflects the shadow 'ClientId1' columns
intentionally: they exist in the prod database today (all NULL),
and EF will rescaffold a drop migration for them on the next
'migrations add' regardless. No data loss.
This commit is contained in:
parent
a44c04ad77
commit
06672c4c90
5 changed files with 4720 additions and 40 deletions
|
|
@ -14,7 +14,7 @@ namespace Yavsc.Blogs.Tests;
|
|||
/// <summary>
|
||||
/// Behavioural tests for <c>BlogAclApiController.PostCircleAuthorizationToBlogPost</c>:
|
||||
/// <c>POST /api/v1/blogacl</c> with a JSON body of
|
||||
/// <c>CircleAuthorizationToBlogPost</c> (CircleId + BlogPostId + Comment).
|
||||
/// <c>CircleAuthorizationToBlogPost</c> (CircleId + BlogPostId).
|
||||
///
|
||||
/// <para>Same fixture as <see cref="CircleMembersApiTests"/>:
|
||||
/// <see cref="BlogsWebServerFixture"/> provides a SQLite
|
||||
|
|
@ -69,11 +69,11 @@ public sealed class BlogAclApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
/// <summary>Create a circle owned by <paramref name="ownerId"/>
|
||||
/// directly in the SQLite store and return its server-assigned
|
||||
/// id.</summary>
|
||||
private long SeedCircle(string ownerId, string name)
|
||||
private long SeedCircle(string ownerId, string name, bool isPublic = false)
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
var circle = new Circle { OwnerId = ownerId, Name = name };
|
||||
var circle = new Circle { OwnerId = ownerId, Name = name, Public = isPublic };
|
||||
db.Circle.Add(circle);
|
||||
db.SaveChanges();
|
||||
return circle.Id;
|
||||
|
|
@ -149,7 +149,6 @@ public sealed class BlogAclApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
{
|
||||
CircleId = circleId,
|
||||
BlogPostId = postId,
|
||||
Comment = true,
|
||||
};
|
||||
|
||||
var response = await http.PostAsJsonAsync(BlogAclUrl(), payload);
|
||||
|
|
@ -158,4 +157,44 @@ public sealed class BlogAclApiTests : IClassFixture<BlogsWebServerFixture>
|
|||
// CreatedAtRoute("GetCircleAuthorizationToBlogPost", ...)).
|
||||
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reproduces the prod 500 logged on 2026-08-21 on mercure:
|
||||
/// <c>InvalidOperationException: The value of
|
||||
/// 'CircleAuthorizationToBlogPost.BlogPostId' is unknown</c>
|
||||
/// when <see cref="PostAclDialogViewModel.AddAsync"/> POSTs the
|
||||
/// shape <c>{ "circleId": <id> }</c> — the exact body the
|
||||
/// PostIt client builds from <see cref="CircleAuthorization"/>
|
||||
/// (which only carries <c>CircleId</c>). The server deserialises
|
||||
/// it into <see cref="CircleAuthorizationToBlogPost"/>, leaves
|
||||
/// <c>BlogPostId</c> at its <c>default(long) = 0</c>, attaches
|
||||
/// no <c>Target</c> navigation, and EF Core refuses to INSERT
|
||||
/// during <c>PrepareToSave()</c>. The fix lives in PostIt
|
||||
/// (enrich the payload with <c>blogPostId</c> + <c>comment</c>)
|
||||
/// and on the wire DTO (<see cref="CircleAuthorization"/> must
|
||||
/// carry those fields); the server validates. Until that ships,
|
||||
/// this test stays red.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task PostCircleAuthorization_returns_201_when_payload_mirrors_PostIt_shape_against_existing_circle_named_test()
|
||||
{
|
||||
ResetDatabaseWithAlice();
|
||||
// The prod circle already exists with Name="test", Public=true,
|
||||
// owned by the caller. We seed the same shape pre-POST so the
|
||||
// test reproduces the prod scenario end-to-end.
|
||||
var circleId = SeedCircle("alice", "test", isPublic: true);
|
||||
var postId = SeedBlogPost("alice", "Billet ACL test");
|
||||
using var http = NewClient("alice");
|
||||
|
||||
// Exact wire shape PostIt sends today:
|
||||
// { "circleId": <id> } — no blogPostId, no comment.
|
||||
var payload = new Dictionary<string, object>
|
||||
{
|
||||
["circleId"] = circleId,
|
||||
};
|
||||
|
||||
var response = await http.PostAsJsonAsync(BlogAclUrl(), payload);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4645
src/Yavsc.Org/Migrations/20260820232152_DropCommentFromCircleAuthorizationToBlogPost.Designer.cs
generated
Normal file
4645
src/Yavsc.Org/Migrations/20260820232152_DropCommentFromCircleAuthorizationToBlogPost.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class DropCommentFromCircleAuthorizationToBlogPost : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Comment",
|
||||
table: "CircleAuthorizationToBlogPost");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Comment",
|
||||
table: "CircleAuthorizationToBlogPost",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -476,9 +476,6 @@ namespace Yavsc.Migrations
|
|||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ClientId1")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("GrantType")
|
||||
.HasColumnType("text");
|
||||
|
||||
|
|
@ -486,8 +483,6 @@ namespace Yavsc.Migrations
|
|||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ClientId1");
|
||||
|
||||
b.ToTable("ClientGrantTypes");
|
||||
});
|
||||
|
||||
|
|
@ -583,9 +578,6 @@ namespace Yavsc.Migrations
|
|||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ClientId1")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.HasColumnType("text");
|
||||
|
||||
|
|
@ -593,8 +585,6 @@ namespace Yavsc.Migrations
|
|||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ClientId1");
|
||||
|
||||
b.ToTable("ClientRedirectUris");
|
||||
});
|
||||
|
||||
|
|
@ -609,9 +599,6 @@ namespace Yavsc.Migrations
|
|||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ClientId1")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.HasColumnType("text");
|
||||
|
||||
|
|
@ -619,8 +606,6 @@ namespace Yavsc.Migrations
|
|||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ClientId1");
|
||||
|
||||
b.ToTable("ClientScopes");
|
||||
});
|
||||
|
||||
|
|
@ -1096,9 +1081,6 @@ namespace Yavsc.Migrations
|
|||
b.Property<long>("BlogPostId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("Comment")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("CircleId", "BlogPostId");
|
||||
|
||||
b.HasIndex("BlogPostId");
|
||||
|
|
@ -3460,16 +3442,12 @@ namespace Yavsc.Migrations
|
|||
|
||||
modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null)
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedGrantTypes")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId1");
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
|
|
@ -3520,31 +3498,23 @@ namespace Yavsc.Migrations
|
|||
|
||||
modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null)
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("RedirectUris")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId1");
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b =>
|
||||
{
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null)
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany("AllowedScopes")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId1");
|
||||
|
||||
b.Navigation("Client");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,5 @@ namespace Yavsc.Models.Access
|
|||
[ForeignKey("CircleId")]
|
||||
public virtual Circle Allowed { get; set; }
|
||||
|
||||
public bool Comment { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue