test: close illegal-state escape hatches in spec type-safety (WP-71)
ESLint blanket-exempted every *.spec.ts from the any ban, and no gate type-checked spec files at all (ng test is transpile-only), so a wrong cast in a test could never fail the build. 76 `as any` + 12 `as Extract<>` state-narrowing casts in the three biggest wizard specs read one variant's fields off a whole-union value: if the reducer returned the wrong variant, the assertion silently read undefined instead of failing. expectTag(state, tag) (libs/shared/src/testing/expect-tag.ts) asserts and narrows in one call, replacing every one of those casts. Removes the spec-file any exemption, adds `npm run typecheck` (tsc --noEmit over each project's tsconfig.spec.json) to CI, and forbids production code from importing libs/shared/src/testing via dependency-cruiser. Backend: AanvraagBuilder now models ZaakUrl (closing the last post-Build() mutation) and guards AtStep; null-forgiving `!` on endpoint assertions replaced with Assert.NotNull so a null DTO fails by name, not NullReferenceException. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,8 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
{
|
||||
BriefStore.Reset();
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
return view!.Brief;
|
||||
Assert.NotNull(view);
|
||||
return view.Brief;
|
||||
}
|
||||
|
||||
private HttpRequestMessage Post(string path, string? role = null, object? body = null)
|
||||
@@ -64,8 +65,9 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
{
|
||||
await Get();
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
Assert.NotNull(view);
|
||||
// global passages + the arts-scoped one; no other-beroep passages leak in.
|
||||
Assert.Contains(view!.AvailablePassages, p => p.PassageId == "p-kern-arts");
|
||||
Assert.Contains(view.AvailablePassages, p => p.PassageId == "p-kern-arts");
|
||||
Assert.All(view.AvailablePassages, p => Assert.True(p.Scope == "global" || p.Beroep == "arts"));
|
||||
|
||||
// Guided-drafting tags (WP-brief-v3): positief + negatief + reason-specific negatief.
|
||||
@@ -78,9 +80,10 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
{
|
||||
await Get();
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
Assert.NotNull(view);
|
||||
// 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));
|
||||
@@ -98,7 +101,8 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
|
||||
var body = await res.Content.ReadFromJsonAsync<RevealBigNummerResponse>();
|
||||
Assert.Equal("19012345601", body!.BigNummer);
|
||||
Assert.NotNull(body);
|
||||
Assert.Equal("19012345601", body.BigNummer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -147,13 +151,15 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
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));
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
Assert.NotNull(view);
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(view.Brief));
|
||||
|
||||
var res = await _client.SendAsync(Post("/api/v1/brief/submit"));
|
||||
res.EnsureSuccessStatusCode();
|
||||
var submitted = await res.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.Equal("submitted", submitted!.Brief.Status.Tag);
|
||||
Assert.NotNull(submitted);
|
||||
Assert.Equal("submitted", submitted.Brief.Status.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -168,7 +174,9 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
|
||||
var res = await _client.SendAsync(Post("/api/v1/brief/approve", role: "approver"));
|
||||
res.EnsureSuccessStatusCode();
|
||||
Assert.Equal("approved", (await res.Content.ReadFromJsonAsync<BriefViewDto>())!.Brief.Status.Tag);
|
||||
var approved = await res.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.NotNull(approved);
|
||||
Assert.Equal("approved", approved.Brief.Status.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -178,9 +186,11 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
await _client.SendAsync(Post("/api/v1/brief/submit"));
|
||||
|
||||
var rejected = await (await _client.SendAsync(
|
||||
Post("/api/v1/brief/reject", role: "approver", body: new RejectBriefRequest("Graag aanvullen.")))).Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.Equal("rejected", rejected!.Brief.Status.Tag);
|
||||
var rejectRes = await _client.SendAsync(
|
||||
Post("/api/v1/brief/reject", role: "approver", body: new RejectBriefRequest("Graag aanvullen.")));
|
||||
var rejected = await rejectRes.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.NotNull(rejected);
|
||||
Assert.Equal("rejected", rejected.Brief.Status.Tag);
|
||||
Assert.Equal("Graag aanvullen.", rejected.Brief.Status.Comments);
|
||||
}
|
||||
|
||||
@@ -194,8 +204,10 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
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>();
|
||||
Assert.Equal("draft", reopened!.Brief.Status.Tag);
|
||||
var putRes = await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
var reopened = await putRes.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.NotNull(reopened);
|
||||
Assert.Equal("draft", reopened.Brief.Status.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -211,7 +223,9 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
await _client.SendAsync(Post("/api/v1/brief/approve", role: "approver"));
|
||||
var res = await _client.SendAsync(Post("/api/v1/brief/send"));
|
||||
res.EnsureSuccessStatusCode();
|
||||
Assert.Equal("sent", (await res.Content.ReadFromJsonAsync<BriefViewDto>())!.Brief.Status.Tag);
|
||||
var sent = await res.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.NotNull(sent);
|
||||
Assert.Equal("sent", sent.Brief.Status.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -219,7 +233,8 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
{
|
||||
var brief = await Get();
|
||||
var view = await _client.GetFromJsonAsync<BriefViewDto>("/api/v1/brief");
|
||||
Assert.True(view!.Decisions.CanEdit); // default (no X-Role) = drafter, draft status
|
||||
Assert.NotNull(view);
|
||||
Assert.True(view.Decisions.CanEdit); // default (no X-Role) = drafter, draft status
|
||||
Assert.False(view.Decisions.CanApprove);
|
||||
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
@@ -228,7 +243,8 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
var asApprover = await _client.SendAsync(
|
||||
new HttpRequestMessage(HttpMethod.Get, "/api/v1/brief") { Headers = { { "X-Role", "approver" } } });
|
||||
var approverView = await asApprover.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.True(approverView!.Decisions.CanApprove);
|
||||
Assert.NotNull(approverView);
|
||||
Assert.True(approverView.Decisions.CanApprove);
|
||||
Assert.False(approverView.Decisions.CanEdit); // approver never edits
|
||||
}
|
||||
|
||||
@@ -236,11 +252,13 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
public async Task Me_returns_no_capabilities_for_drafter_and_the_brief_set_for_approver()
|
||||
{
|
||||
var asDrafter = await _client.GetFromJsonAsync<MeDto>("/api/v1/me");
|
||||
Assert.Empty(asDrafter!.Capabilities);
|
||||
Assert.NotNull(asDrafter);
|
||||
Assert.Empty(asDrafter.Capabilities);
|
||||
|
||||
var res = await _client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/api/v1/me") { Headers = { { "X-Role", "approver" } } });
|
||||
var asApprover = await res.Content.ReadFromJsonAsync<MeDto>();
|
||||
Assert.Equal(new[] { "brief:approve", "brief:reject", "brief:send" }, asApprover!.Capabilities);
|
||||
Assert.NotNull(asApprover);
|
||||
Assert.Equal(new[] { "brief:approve", "brief:reject", "brief:send" }, asApprover.Capabilities);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -254,7 +272,8 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
var res = await _client.SendAsync(Post("/api/v1/brief/reset"));
|
||||
res.EnsureSuccessStatusCode();
|
||||
var view = await res.Content.ReadFromJsonAsync<BriefViewDto>();
|
||||
Assert.Equal("draft", view!.Brief.Status.Tag);
|
||||
Assert.NotNull(view);
|
||||
Assert.Equal("draft", view.Brief.Status.Tag);
|
||||
var aanhef = view.Brief.Sections.Single(s => s.SectionKey == "aanhef");
|
||||
Assert.True(aanhef.Locked);
|
||||
Assert.NotEmpty(aanhef.Blocks);
|
||||
|
||||
Reference in New Issue
Block a user