test: split multi-assertion specs into single-behavior tests

One behavior per test across FE machine/store specs and backend endpoint
tests, so a failure names exactly what broke.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-20 20:33:25 +02:00
co-authored by Claude Opus 4.8
parent 5cae44f163
commit 55a0a2d166
48 changed files with 178 additions and 34 deletions
@@ -55,7 +55,7 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
}
[Fact]
public async Task Publish_increments_version_appends_history_and_counts_unsent_briefs()
public async Task Publish_increments_the_version()
{
ResetStores();
// One unsent brief for this sub-org (GetOrCreate on first read).
@@ -65,16 +65,42 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
res.EnsureSuccessStatusCode();
var published = await res.Content.ReadFromJsonAsync<PublishOrgTemplateResponse>();
Assert.Equal(2, published!.Version);
Assert.Equal(1, published.AffectedUnsentBriefs);
var view = await AdminView();
Assert.Equal(2, view.PublishedVersion);
}
[Fact]
public async Task Publish_appends_to_the_version_history()
{
ResetStores();
await _client.GetAsync("/api/v1/brief");
var res = await _client.SendAsync(Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/publish", role: "admin"));
res.EnsureSuccessStatusCode();
var view = await AdminView();
Assert.Equal(new[] { 1, 2 }, view.History.Select(h => h.Version));
}
[Fact]
public async Task Publish_counts_the_unsent_briefs_it_affects()
{
ResetStores();
// One unsent brief for this sub-org (GetOrCreate on first read).
await _client.GetAsync("/api/v1/brief");
var res = await _client.SendAsync(Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/publish", role: "admin"));
res.EnsureSuccessStatusCode();
var published = await res.Content.ReadFromJsonAsync<PublishOrgTemplateResponse>();
Assert.Equal(1, published!.AffectedUnsentBriefs);
var view = await AdminView();
Assert.Equal(1, view.UnsentBriefs);
}
[Fact]
public async Task Save_draft_validates_margins_and_round_trips()
public async Task Save_draft_validates_margins()
{
ResetStores();
var draft = (await AdminView()).Draft;
@@ -83,6 +109,13 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
Assert.Equal(HttpStatusCode.BadRequest, (await _client.SendAsync(
Req(HttpMethod.Put, $"/api/v1/admin/org-template/{Registers}", role: "admin",
body: new SaveOrgTemplateRequest(invalid)))).StatusCode);
}
[Fact]
public async Task Save_draft_round_trips_the_edited_values()
{
ResetStores();
var draft = (await AdminView()).Draft;
var valid = draft with { OrgName = "BIG-register (nieuw)", Margins = new MarginsDto(30, 20, 20, 25) };
var res = await _client.SendAsync(Req(HttpMethod.Put, $"/api/v1/admin/org-template/{Registers}", role: "admin",
@@ -115,11 +148,12 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/rollback/99", role: "admin"))).StatusCode);
}
[Fact]
public async Task Sent_brief_keeps_its_pinned_template_while_an_unsent_brief_follows_a_republish()
// Walk one brief all the way to sent under template v1, then republish the org
// template under a new name. Both invariant tests below share this exact
// precondition (the stores are process-global, so each sets it up).
private async Task WalkBriefToSentThenRepublish()
{
ResetStores();
// Walk one brief to sent under template v1.
var brief = (await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief"))!.Brief;
var filled = brief.Sections
.Select(s => new LetterSectionDto(s.SectionKey, s.Title, s.Required,
@@ -138,13 +172,25 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
await _client.SendAsync(Req(HttpMethod.Put, $"/api/v1/admin/org-template/{Registers}", role: "admin",
body: new SaveOrgTemplateRequest(draft with { OrgName = "Hertitelde organisatie" })));
await _client.SendAsync(Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/publish", role: "admin"));
}
// The sent brief still renders v1 with the old name (immutable)...
[Fact]
public async Task Sent_brief_keeps_its_pinned_template_after_a_republish()
{
await WalkBriefToSentThenRepublish();
// The sent brief still renders v1 with the old name (immutable).
var sentView = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
Assert.Equal(1, sentView!.OrgTemplate.Version);
Assert.Equal("BIG-register", sentView.OrgTemplate.OrgName);
}
// ...while a fresh (unsent) brief follows the new published version.
[Fact]
public async Task Unsent_brief_follows_a_republish()
{
await WalkBriefToSentThenRepublish();
// A fresh (unsent) brief follows the new published version.
var freshView = await (await _client.SendAsync(Req(HttpMethod.Post, "/api/v1/brief/reset"))).Content.ReadFromJsonAsync<BriefViewDto>();
Assert.Equal(2, freshView!.OrgTemplate.Version);
Assert.Equal("Hertitelde organisatie", freshView.OrgTemplate.OrgName);