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:
@@ -44,7 +44,7 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_creates_a_draft_from_the_template_with_scoped_passages()
|
||||
public async Task Get_creates_a_draft_with_expected_sections_locked_and_empty()
|
||||
{
|
||||
var brief = await Get();
|
||||
Assert.Equal("draft", brief.Status.Tag);
|
||||
@@ -57,7 +57,12 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
var kern = brief.Sections.Single(s => s.SectionKey == "kern");
|
||||
Assert.False(kern.Locked);
|
||||
Assert.Empty(kern.Blocks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_offers_only_global_and_arts_scoped_besluit_tagged_passages()
|
||||
{
|
||||
await Get();
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
// global passages + the arts-scoped one; no other-beroep passages leak in.
|
||||
Assert.Contains(view!.AvailablePassages, p => p.PassageId == "p-kern-arts");
|
||||
@@ -66,10 +71,16 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
// Guided-drafting tags (WP-brief-v3): positief + negatief + reason-specific negatief.
|
||||
Assert.Contains(view.AvailablePassages, p => p.Besluit == "positief");
|
||||
Assert.Contains(view.AvailablePassages, p => p.Besluit == "negatief" && p.Reason == "onvoldoende_scholing");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_joins_the_case_context_with_the_BIG_nummer_masked()
|
||||
{
|
||||
await Get();
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
// Case context is joined onto the screen DTO for the behandel scherm header.
|
||||
// The BIG-nummer ships MASKED by default (PRD-0002 §5c) — reveal is a separate call.
|
||||
Assert.Equal("********601", view.CaseContext.BigNummer);
|
||||
Assert.Equal("********601", view!.CaseContext.BigNummer);
|
||||
Assert.Equal("arts", view.CaseContext.Beroep);
|
||||
Assert.False(string.IsNullOrWhiteSpace(view.CaseContext.ZorgverlenerNaam));
|
||||
Assert.False(string.IsNullOrWhiteSpace(view.CaseContext.AanvraagReferentie));
|
||||
@@ -125,12 +136,17 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Submit_blocks_on_empty_required_section_then_succeeds_when_filled()
|
||||
public async Task Submit_blocks_on_empty_required_section()
|
||||
{
|
||||
await Get();
|
||||
// Nothing filled yet → required sections empty → 409.
|
||||
Assert.Equal(HttpStatusCode.Conflict, (await _client.SendAsync(Post("/api/v1/brief/submit"))).StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Submit_succeeds_when_required_sections_filled()
|
||||
{
|
||||
await Get();
|
||||
var brief = (await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief"))!.Brief;
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
|
||||
@@ -156,7 +172,7 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Reject_returns_comments_and_editing_reopens_to_draft()
|
||||
public async Task Reject_returns_comments()
|
||||
{
|
||||
var brief = await Get();
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
@@ -166,6 +182,16 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
Post("/api/v1/brief/reject", role: "approver", body: new RejectBriefRequest("Graag aanvullen.")))).Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.Equal("rejected", rejected!.Brief.Status.Tag);
|
||||
Assert.Equal("Graag aanvullen.", rejected.Brief.Status.Comments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Editing_a_rejected_letter_reopens_it_to_draft()
|
||||
{
|
||||
var brief = await Get();
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
await _client.SendAsync(Post("/api/v1/brief/submit"));
|
||||
await _client.SendAsync(
|
||||
Post("/api/v1/brief/reject", role: "approver", body: new RejectBriefRequest("Graag aanvullen.")));
|
||||
|
||||
// A drafter save on a rejected letter reopens it to draft.
|
||||
var reopened = await (await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief))).Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -81,12 +81,17 @@ public class PreviewEndpointTests(TestWebApplicationFactory factory) : IClassFix
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Proefbrief_is_admin_only_and_renders_the_draft_template()
|
||||
public async Task Proefbrief_is_admin_only()
|
||||
{
|
||||
ResetStores();
|
||||
Assert.Equal(HttpStatusCode.Forbidden,
|
||||
(await _client.GetAsync($"/api/v1/admin/org-template/{Registers}/preview")).StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Proefbrief_renders_the_draft_template_with_a_watermark()
|
||||
{
|
||||
ResetStores();
|
||||
var res = await _client.SendAsync(Req(HttpMethod.Get, $"/api/v1/admin/org-template/{Registers}/preview", role: "admin"));
|
||||
res.EnsureSuccessStatusCode();
|
||||
var html = await res.Content.ReadAsStringAsync();
|
||||
|
||||
Reference in New Issue
Block a user